diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx index 6ca6538..b10b070 100644 --- a/frontend/app/page.tsx +++ b/frontend/app/page.tsx @@ -1,13 +1,76 @@ 'use client' -import { useState } from 'react' -import { askQuestion, listDocuments, listMessages } from '@/lib/api' +import { useEffect, useState } from 'react' +import { + askQuestion, + getDocument, + listDocuments, + type Document, + type DocumentDetail, +} from '@/lib/api' + +function formatDate(value?: string | null) { + if (!value) return '' + const date = new Date(value) + if (Number.isNaN(date.getTime())) return '' + return date.toLocaleDateString('en-AU', { + day: 'numeric', + month: 'short', + year: 'numeric', + }) +} export default function Home() { const [question, setQuestion] = useState('') const [answer, setAnswer] = useState('') const [loading, setLoading] = useState(false) const [activeTab, setActiveTab] = useState<'ask' | 'documents' | 'messages'>('ask') + const [documents, setDocuments] = useState([]) + const [documentsTotal, setDocumentsTotal] = useState(0) + const [documentsLoading, setDocumentsLoading] = useState(false) + const [documentsError, setDocumentsError] = useState('') + const [selectedDocument, setSelectedDocument] = useState(null) + const [detailLoading, setDetailLoading] = useState(false) + const [detailError, setDetailError] = useState('') + + useEffect(() => { + if (activeTab !== 'documents') return + let cancelled = false + setDocumentsLoading(true) + setDocumentsError('') + listDocuments() + .then((data) => { + if (cancelled) return + setDocuments(data.items) + setDocumentsTotal(data.total) + }) + .catch(() => { + if (cancelled) return + setDocuments([]) + setDocumentsTotal(0) + setDocumentsError('Could not load documents. Check the backend is running and try again.') + }) + .finally(() => { + if (!cancelled) setDocumentsLoading(false) + }) + return () => { + cancelled = true + } + }, [activeTab]) + + const handleSelectDocument = async (doc: Document) => { + setDetailLoading(true) + setDetailError('') + try { + const detail = await getDocument(doc.id) + setSelectedDocument(detail) + } catch { + setSelectedDocument(null) + setDetailError('Could not load this document. Try again.') + } finally { + setDetailLoading(false) + } + } const handleAsk = async () => { if (!question.trim()) return @@ -135,20 +198,91 @@ export default function Home() { {activeTab === 'documents' && (
-

+

Knowledge Base Documents

-

- Documents are indexed for retrieval-augmented generation. - Use the API to add new documents to the knowledge base. +

+ Indexed documents used for retrieval-augmented generation. Select one to read its full content.

-
- - POST /api/documents -
- {`{ "title": "...", "content": "...", "source": "..." }`} -
-
+ + {documentsLoading && ( +

Loading documents...

+ )} + + {!documentsLoading && documentsError && ( +
+

{documentsError}

+
+ )} + + {!documentsLoading && !documentsError && documents.length === 0 && ( +

+ No documents yet. Add one with POST /api/documents and it will appear here. +

+ )} + + {!documentsLoading && !documentsError && documents.length > 0 && ( + <> +

+ {documentsTotal} {documentsTotal === 1 ? 'document' : 'documents'} +

+
    + {documents.map((doc) => ( +
  • + +
  • + ))} +
+ + )} + + {detailLoading && ( +

Loading document...

+ )} + + {!detailLoading && detailError && ( +
+

{detailError}

+
+ )} + + {!detailLoading && !detailError && selectedDocument && ( +
+

+ {selectedDocument.title} +

+

+ {selectedDocument.source} + {formatDate(selectedDocument.created_at) && + ` - ${formatDate(selectedDocument.created_at)}`} +

+

{selectedDocument.content}

+
+ )}
)}