import React from 'react'; import { Header, Segment, Table, Button, Icon, Label, Message, Modal, Pagination, Dropdown, Input } from 'semantic-ui-react'; import superagent from 'superagent'; import ExperimentalWrapper from './ExperimentalWrapper.jsx'; export default class KBDocuments extends React.Component { constructor(props) { super(props); this.state = { documents: [], pagination: { limit: 20, offset: 0, total: 0 }, loading: true, error: null, selectedDocument: null, documentDetails: null, detailsModalOpen: false, deleteModalOpen: false, documentToDelete: null, filterSourceType: '', searchQuery: '' }; } componentDidMount() { this.loadDocuments(); } async loadDocuments() { const { pagination, filterSourceType } = this.state; this.setState({ loading: true, error: null }); try { let url = `/api/admin/kb/documents?limit=${pagination.limit}&offset=${pagination.offset}`; if (filterSourceType) { url += `&sourceType=${filterSourceType}`; } const res = await superagent.get(url); this.setState({ documents: res.body.documents || [], pagination: res.body.pagination || pagination, loading: false }); } catch (error) { console.error('Failed to load documents:', error); this.setState({ error: error.response?.body?.error || error.message, loading: false }); } } async loadDocumentDetails(documentId) { this.setState({ detailsModalOpen: true, documentDetails: null }); try { const res = await superagent.get(`/api/admin/kb/document/${documentId}`); this.setState({ documentDetails: res.body }); } catch (error) { console.error('Failed to load document details:', error); this.setState({ error: error.response?.body?.error || error.message, detailsModalOpen: false }); } } async deleteDocument() { const { documentToDelete } = this.state; if (!documentToDelete) return; try { await superagent.delete(`/api/admin/kb/document/${documentToDelete.source_type}/${documentToDelete.source_id}`); this.setState({ deleteModalOpen: false, documentToDelete: null }); this.loadDocuments(); } catch (error) { console.error('Failed to delete document:', error); this.setState({ error: error.response?.body?.error || error.message, deleteModalOpen: false }); } } handlePageChange(e, { activePage }) { const { pagination } = this.state; this.setState({ pagination: { ...pagination, offset: (activePage - 1) * pagination.limit } }, () => this.loadDocuments()); } handleFilterChange(e, { value }) { this.setState({ filterSourceType: value, pagination: { ...this.state.pagination, offset: 0 } }, () => this.loadDocuments()); } handlePageSizeChange(e, { value }) { this.setState({ pagination: { ...this.state.pagination, limit: value, offset: 0 } }, () => this.loadDocuments()); } getSourceColor(sourceType) { const colors = { file: 'blue', github: 'black', slack: 'purple', google_drive: 'green', notion: 'orange', discord: 'violet' }; return colors[sourceType] || 'grey'; } formatDate(dateString) { if (!dateString) return '-'; const date = new Date(dateString); return date.toLocaleDateString() + ' ' + date.toLocaleTimeString(); } renderDocumentDetailsModal() { const { detailsModalOpen, documentDetails } = this.state; return ( this.setState({ detailsModalOpen: false, documentDetails: null })} size="large" > Document Details {!documentDetails ? ( Loading... ) : ( <> Metadata ID {documentDetails.document.id} Title {documentDetails.document.title || '-'} Source Type {documentDetails.document.source_type} Source ID {documentDetails.document.source_id} Source URL {documentDetails.document.source_url ? ( {documentDetails.document.source_url} ) : '-'} Visibility {documentDetails.document.visibility || 'internal'} Indexed At {this.formatDate(documentDetails.document.indexed_at)} Chunks ({documentDetails.chunks?.length || 0}) {documentDetails.chunks?.map((chunk, index) => ( Chunk {chunk.chunk_index + 1} ({chunk.content_tokens} tokens) {chunk.content} ))} > )} this.setState({ detailsModalOpen: false, documentDetails: null })}> Close ); } renderDeleteModal() { const { deleteModalOpen, documentToDelete } = this.state; return ( this.setState({ deleteModalOpen: false, documentToDelete: null })} size="small" > Delete Document Are you sure you want to delete this document? {documentToDelete && ( {documentToDelete.title || 'Untitled'} {documentToDelete.source_type} {' '} {documentToDelete.source_id} )} This will permanently delete the document and all its indexed chunks. this.setState({ deleteModalOpen: false, documentToDelete: null })}> Cancel this.deleteDocument()}> Delete ); } render() { const { documents, pagination, loading, error, filterSourceType } = this.state; const sourceTypeOptions = [ { key: 'all', text: 'All Sources', value: '' }, { key: 'file', text: 'File', value: 'file' }, { key: 'github', text: 'GitHub', value: 'github' }, { key: 'slack', text: 'Slack', value: 'slack' }, { key: 'google_drive', text: 'Google Drive', value: 'google_drive' }, { key: 'notion', text: 'Notion', value: 'notion' }, { key: 'discord', text: 'Discord', value: 'discord' } ]; const pageSizeOptions = [ { key: '20', text: '20 per page', value: 20 }, { key: '50', text: '50 per page', value: 50 }, { key: '100', text: '100 per page', value: 100 } ]; const totalPages = Math.ceil((pagination.total || documents.length) / pagination.limit); const currentPage = Math.floor(pagination.offset / pagination.limit) + 1; return ( Knowledge Base Documents View and manage indexed documents Upload Document this.loadDocuments()}> {pagination.total > 0 && ( {pagination.total.toLocaleString()} documents )} {error && ( Error {error} )} Title Source Source ID Indexed At Actions {documents.length === 0 ? ( No documents found ) : ( documents.map(doc => ( { e.preventDefault(); this.loadDocumentDetails(doc.id); }} > {doc.title || 'Untitled'} {doc.source_type} {doc.source_id.length > 30 ? doc.source_id.substring(0, 30) + '...' : doc.source_id} {this.formatDate(doc.indexed_at)} this.loadDocumentDetails(doc.id)} title="View Details" /> this.setState({ deleteModalOpen: true, documentToDelete: doc })} title="Delete" /> )) )} {totalPages > 0 && ( Showing {pagination.offset + 1} - {Math.min(pagination.offset + pagination.limit, pagination.total)} of {pagination.total} {totalPages > 1 && ( )} )} {this.renderDocumentDetailsModal()} {this.renderDeleteModal()} ); } }
{documentDetails.document.id}
{documentDetails.document.source_id}
{chunk.content}
Are you sure you want to delete this document?
{documentToDelete.source_id}
{error}
No documents found
{doc.source_id.length > 30 ? doc.source_id.substring(0, 30) + '...' : doc.source_id}