import React, { useState, useEffect } from 'react'; import { useAuth } from '../context/AuthContext'; import type { DocumentInfo } from '../types/api'; import { Upload, FilePlus, Activity, Trash2, Eye, Globe, X, FileText, Link, Hash, BookOpen } from 'lucide-react'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; const API_BASE = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8000/api'; interface PreviewData { filename: string; file_type: string; word_count: number; char_count: number; content: string; } const Process: React.FC = () => { const { token, logout } = useAuth(); const [documents, setDocuments] = useState([]); const [loadingDocs, setLoadingDocs] = useState(true); const [uploading, setUploading] = useState(false); const [file, setFile] = useState(null); const [url, setUrl] = useState(''); const [message, setMessage] = useState(''); // Preview modal state const [previewOpen, setPreviewOpen] = useState(false); const [previewData, setPreviewData] = useState(null); const [previewLoading, setPreviewLoading] = useState(false); interface TaskState { status: string; progress?: { current_chunk?: number; total_chunks?: number; file?: string; }; } const [tasks, setTasks] = useState>({}); const fetchDocuments = async () => { setLoadingDocs(true); try { const res = await fetch(`${API_BASE}/documents`, { headers: { Authorization: `Bearer ${token}` } }); if (res.status === 401) { logout(); return; } if (res.ok) { const data = await res.json(); setDocuments(data.documents); } } catch (err) { console.error('Failed to fetch docs', err); } finally { setLoadingDocs(false); } }; useEffect(() => { fetchDocuments(); const interval = setInterval(fetchDocuments, 10000); return () => clearInterval(interval); }, [token]); useEffect(() => { const activeTasks = Object.keys(tasks).filter( tid => tasks[tid].status !== 'completed' && tasks[tid].status !== 'failure' ); if (activeTasks.length === 0) return; const interval = setInterval(() => { activeTasks.forEach(async (tid) => { try { const res = await fetch(`${API_BASE}/documents/status/${tid}`, { headers: { Authorization: `Bearer ${token}` } }); if (res.status === 401) { logout(); return; } if (res.ok) { const data = await res.json(); setTasks(prev => ({ ...prev, [tid]: { status: data.status, progress: data.progress } })); if (data.status === 'completed' || data.status === 'failure') fetchDocuments(); } } catch (e) {} }); }, 2000); return () => clearInterval(interval); }, [tasks, token]); const handleFileChange = (e: React.ChangeEvent) => { if (e.target.files && e.target.files.length > 0) setFile(e.target.files[0]); }; const handleUpload = async (e: React.FormEvent) => { e.preventDefault(); if (!file) return; setUploading(true); setMessage(''); const formData = new FormData(); formData.append('file', file); try { const res = await fetch(`${API_BASE}/documents/upload`, { method: 'POST', headers: { Authorization: `Bearer ${token}` }, body: formData }); if (res.status === 401) { logout(); return; } const data = await res.json(); if (!res.ok) { setMessage(`UPLOAD FAILED: ${data.detail}`); } else { setMessage('FILE UPLOADED. INGESTION TASK QUEUED.'); if (data.task_id) setTasks(prev => ({ ...prev, [data.task_id]: { status: 'pending' } })); setFile(null); fetchDocuments(); } } catch (err: any) { setMessage(`ERROR: ${err.message}`); } finally { setUploading(false); } }; const handleUrlScrape = async (e: React.FormEvent) => { e.preventDefault(); if (!url.trim()) return; setUploading(true); setMessage(''); try { const res = await fetch(`${API_BASE}/documents/scrape`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` }, body: JSON.stringify({ url: url.trim() }) }); if (res.status === 401) { logout(); return; } const data = await res.json(); if (!res.ok) { setMessage(`SCRAPE FAILED: ${data.detail}`); } else { setMessage('URL SCRAPED. INGESTION TASK QUEUED.'); if (data.task_id) setTasks(prev => ({ ...prev, [data.task_id]: { status: 'pending' } })); setUrl(''); fetchDocuments(); } } catch (err: any) { setMessage(`ERROR: ${err.message}`); } finally { setUploading(false); } }; const handleDelete = async (docId: string) => { if (!window.confirm('WARNING: Delete this document and all its graph data?')) return; try { const res = await fetch(`${API_BASE}/documents/${docId}`, { method: 'DELETE', headers: { Authorization: `Bearer ${token}` } }); if (res.status === 401) { logout(); return; } fetchDocuments(); } catch (err) { console.error(err); } }; const handleView = async (doc: any) => { const fileType = (doc.file_type || '').toLowerCase(); // Text/scraped files → use inline preview modal if (fileType === '.txt' || fileType === '.md' || fileType === '') { setPreviewOpen(true); setPreviewLoading(true); setPreviewData(null); try { const res = await fetch(`${API_BASE}/documents/${doc.id}/preview`, { headers: { Authorization: `Bearer ${token}` } }); if (res.status === 401) { logout(); return; } if (res.ok) { const data = await res.json(); setPreviewData(data); } else { const err = await res.json(); setMessage(`PREVIEW ERROR: ${err.detail}`); setPreviewOpen(false); } } catch (err) { setMessage('ERROR: Failed to load preview.'); setPreviewOpen(false); } finally { setPreviewLoading(false); } return; } // Binary files (PDF) → open in new tab as before const newTab = window.open('', '_blank'); if (!newTab) { setMessage('ERROR: Please allow popups to view PDFs.'); return; } newTab.document.write('
Fetching secure document...
'); try { const res = await fetch(`${API_BASE}/documents/${doc.id}/download`, { headers: { Authorization: `Bearer ${token}` } }); if (res.status === 401) { newTab.close(); logout(); return; } if (res.ok) { const contentType = res.headers.get('content-type') || 'application/pdf'; const blob = await res.blob(); const fileBlob = new Blob([blob], { type: contentType }); const blobUrl = window.URL.createObjectURL(fileBlob); newTab.location.href = blobUrl; } else { newTab.close(); setMessage('ERROR: Document file not found on server.'); } } catch (err) { newTab.close(); setMessage('ERROR: Failed to open document.'); } }; const isTxtDoc = (doc: any) => { const ft = (doc.file_type || '').toLowerCase(); return ft === '.txt' || ft === '.md' || ft === ''; }; return (

INGESTION PIPELINE

DATA PROCESSING & GRAPH CONSTRUCTION

{/* Upload / Scrape Section */}

ADD DOCUMENT


SCRAPE URL

Paste any public URL — articles, docs, Wikipedia pages — to scrape and ingest the content into your knowledge graph.

setUrl(e.target.value)} required style={{ width: '100%', marginBottom: '1rem' }} />
{Object.keys(tasks).length > 0 && (

ACTIVE TASKS

{Object.entries(tasks).map(([tid, taskObj]) => { const pct = (taskObj.progress?.current_chunk && taskObj.progress?.total_chunks) ? Math.round((taskObj.progress.current_chunk / taskObj.progress.total_chunks) * 100) : 0; return (
{tid.substring(0,8)}... {taskObj.status.toUpperCase()}{taskObj.status === 'processing' && pct > 0 ? ` ${pct}%` : ''}
{taskObj.status === 'processing' && taskObj.progress?.total_chunks && (
)}
); })}
)}
{/* Corpus Table */}

CORPUS INDEX

{documents.length === 0 && !loadingDocs ? ( ) : loadingDocs ? ( ) : ( documents.map((doc: any) => ( )) )}
DOCUMENT NAME TYPE SIZE INGESTED ACTIONS

NO DOCUMENTS INDEXED IN GRAPH DATABASE

Upload a file or scrape a URL above to begin.

FETCHING CORPUS DATA...
{isTxtDoc(doc) ? : } {doc.filename} {isTxtDoc(doc) ? 'WEB / TXT' : (doc.file_type || 'PDF').toUpperCase()} {(doc.size_bytes / 1024).toFixed(1)} KB {doc.upload_date?.substring(0,19) || '—'}
{/* Document Preview Modal */} {previewOpen && (
setPreviewOpen(false)}>
e.stopPropagation()}> {/* Modal Header */}
{previewData?.filename || 'Loading...'}
{previewData && (
{previewData.word_count.toLocaleString()} words {(previewData.char_count / 1000).toFixed(1)}K chars WEB SCRAPE
)}
{/* Modal Body */}
{previewLoading ? (
Fetching Content...
) : previewData ? (
{previewData.content}
) : (
Failed to load content.
)}
)} {message && (
{message}
)}
); }; export default Process;