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
| Property | Type | Required | Description |
|---|---|---|---|
type | string | No | Filter by document type |
tags | string[] | No | Filter by tags |
indexed | boolean | No | Filter by indexing status |
autoRefetch | boolean | No | Auto-refetch after mutations (default: true) |
onUploadComplete | (doc: Document) => void | No | Callback when upload completes |
onUploadError | (error: Error) => void | No | Upload error callback |
Return Value
UseDocumentsResult
| Property | Type | Description |
|---|---|---|
documents | Document[] | undefined | Array of documents |
loading | boolean | Loading state |
error | Error | null | Error 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 |
isUploading | boolean | True during upload |
uploadProgress | Record<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>
);
}
Document Search
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
- Learn about useChat hook for RAG queries
- Explore Documents module for SDK usage
- See Documents API endpoints
- Read Document management guide