// ============================================================================ // public/screens/kb.jsx — Knowledge Base (Phase 6A) // ============================================================================ function KnowledgeBaseScreen() { const [colls, setColls] = React.useState(null); const [activeColl, setActive] = React.useState(null); // full collection object const [docs, setDocs] = React.useState([]); const [showCreate, setShowCreate] = React.useState(false); const [showAddDoc, setShowAddDoc] = React.useState(false); const [showQuery, setShowQuery] = React.useState(false); const load = React.useCallback(async () => { const r = await VoaisAPI.get("/api/kb"); if (r.ok && r.data?.ok) setColls(r.data.collections); }, []); React.useEffect(() => { load(); }, [load]); const openCollection = async (id) => { const r = await VoaisAPI.get("/api/kb/" + id); if (r.ok && r.data?.ok) { setActive(r.data.collection); setDocs(r.data.documents); } }; const deleteDoc = async (collId, docId) => { if (!confirm("Delete this document and all its chunks?")) return; await VoaisAPI.del("/api/kb/" + collId + "/documents/" + docId); openCollection(collId); }; const deleteColl = async (id) => { if (!confirm("Delete this collection and ALL its documents?")) return; await VoaisAPI.del("/api/kb/" + id); setActive(null); load(); }; if (colls === null) return ; // Collection detail view if (activeColl) { return (
} onClick={() => { setActive(null); load(); }}>Collections
{activeColl.name} {activeColl.total_docs} docs {activeColl.total_chunks} chunks
} onClick={() => setShowQuery(true)}>Test query } onClick={() => setShowAddDoc(true)}>Add document
{activeColl.description &&
{activeColl.description}
} {/* Documents table */} {docs.map(d => ( ))} {docs.length === 0 && }
DocumentTypeSizeStatusChunksAdded
{d.name}
{d.code}
{d.type} {formatBytes(d.size_bytes)} {d.index_status} {d.chunks_count} {new Date(d.created_at).toLocaleDateString("en-IN", { day: "numeric", month: "short" })} deleteDoc(activeColl.id, d.id)}>
No documents yet. Add one to get started.
{/* Add document modal */} {showAddDoc && setShowAddDoc(false)} onAdded={() => { setShowAddDoc(false); openCollection(activeColl.id); }}/>} {/* Query tester modal */} {showQuery && setShowQuery(false)}/>}
); } // Collection list view return (
{colls.length} collections
} onClick={() => setShowCreate(true)}>New collection
{colls.length === 0 && (

Knowledge Base

Upload product docs, FAQs, and guides. Your AI agents will use this knowledge to answer questions during calls.

} onClick={() => setShowCreate(true)}>Create collection
)}
{colls.map(c => (
openCollection(c.id)}>

{c.name}

{c.description &&
{c.description}
}
{c.total_docs} docs {c.total_chunks} chunks {formatBytes(c.total_size_bytes)}
openCollection(c.id)}>Open deleteColl(c.id)}>
))}
{showCreate && ( setShowCreate(false)} width={460}> { setShowCreate(false); load(); openCollection(id); }}/> )}
); } function CreateCollForm({ onCreated }) { const [name, setName] = React.useState(""); const [desc, setDesc] = React.useState(""); const [sub, setSub] = React.useState(false); const submit = async () => { if (!name.trim()) return; setSub(true); const r = await VoaisAPI.post("/api/kb", { name, description: desc || null }); setSub(false); if (r.ok) onCreated(r.data.collectionId); }; return (
setName(e.target.value)} placeholder="e.g. Mercedes GLE Product Sheets"/>