Spaces:
Running
Running
| /** | |
| * Context Documents Section Component | |
| * | |
| * Main section for managing context documents in the TraceDetailsModal | |
| */ | |
| import React, { useState, useEffect } from "react"; | |
| import { Button } from "@/components/ui/button"; | |
| import { FileText, Plus } from "lucide-react"; | |
| import { useContextDocuments } from "@/hooks/useContextDocuments"; | |
| import { ContextDocumentCard } from "@/components/features/context/ContextDocumentCard"; | |
| import { ContextUploadDialog } from "@/components/features/context/ContextUploadDialog"; | |
| import { ContextDocumentModal } from "@/components/features/context/ContextDocumentModal"; | |
| import { ContextDocument } from "@/types/context"; | |
| interface ContextDocumentsSectionProps { | |
| traceId: string; | |
| showHeader?: boolean; // New prop to control header visibility | |
| triggerAdd?: number; // Prop to trigger add dialog from external button | |
| } | |
| export function ContextDocumentsSection({ | |
| traceId, | |
| showHeader = true, // Default to showing header for backward compatibility | |
| triggerAdd, | |
| }: ContextDocumentsSectionProps) { | |
| const { | |
| documents, | |
| loading, | |
| error, | |
| loadDocuments, | |
| deleteDocument, | |
| updateDocument, | |
| } = useContextDocuments(); | |
| const [isUploadDialogOpen, setIsUploadDialogOpen] = useState(false); | |
| const [selectedDocument, setSelectedDocument] = | |
| useState<ContextDocument | null>(null); | |
| const [isModalOpen, setIsModalOpen] = useState(false); | |
| // Load documents when component mounts or traceId changes | |
| useEffect(() => { | |
| if (traceId) { | |
| loadDocuments(traceId); | |
| } | |
| }, [traceId, loadDocuments]); | |
| // Trigger add dialog when external button is clicked | |
| useEffect(() => { | |
| if (triggerAdd && triggerAdd > 0) { | |
| setIsUploadDialogOpen(true); | |
| } | |
| }, [triggerAdd]); | |
| const handleAddContext = () => { | |
| setIsUploadDialogOpen(true); | |
| }; | |
| const handleDeleteDocument = async (contextId: string) => { | |
| const success = await deleteDocument(traceId, contextId); | |
| if (success) { | |
| // Document has been removed from state by the hook | |
| } | |
| }; | |
| const handleViewEdit = (document: ContextDocument) => { | |
| setSelectedDocument(document); | |
| setIsModalOpen(true); | |
| }; | |
| const handleModalClose = () => { | |
| setIsModalOpen(false); | |
| setSelectedDocument(null); | |
| }; | |
| const handleDocumentSave = async (updates: any) => { | |
| if (!selectedDocument) return false; | |
| const updatedDocument = await updateDocument( | |
| traceId, | |
| selectedDocument.id, | |
| updates | |
| ); | |
| if (updatedDocument) { | |
| setSelectedDocument(updatedDocument); | |
| return true; | |
| } | |
| return false; | |
| }; | |
| const handleDocumentDelete = async () => { | |
| if (!selectedDocument) return false; | |
| const success = await deleteDocument(traceId, selectedDocument.id); | |
| if (success) { | |
| setIsModalOpen(false); | |
| setSelectedDocument(null); | |
| } | |
| return success; | |
| }; | |
| if (error) { | |
| return ( | |
| <div className="p-4 rounded-lg border border-destructive/20 bg-destructive/5"> | |
| <div className="flex items-center gap-2 text-destructive mb-2"> | |
| <FileText className="h-5 w-5" /> | |
| <span className="font-medium">Context Documents - Error</span> | |
| </div> | |
| <p className="text-sm text-destructive mb-2">{error}</p> | |
| <Button | |
| variant="outline" | |
| size="sm" | |
| onClick={() => loadDocuments(traceId)} | |
| > | |
| Retry | |
| </Button> | |
| </div> | |
| ); | |
| } | |
| return ( | |
| <> | |
| <div className="space-y-4"> | |
| {showHeader && ( | |
| <div className="flex items-center justify-between"> | |
| <div className="flex items-center gap-2"> | |
| <FileText className="h-5 w-5" /> | |
| <span className="font-medium"> | |
| Context Documents ({documents.length}) | |
| </span> | |
| </div> | |
| <Button | |
| onClick={handleAddContext} | |
| disabled={loading} | |
| size="sm" | |
| className="bg-primary hover:bg-primary/90" | |
| data-context-add-button | |
| > | |
| <Plus className="h-3 w-3 mr-2" /> | |
| Add Context | |
| </Button> | |
| </div> | |
| )} | |
| {loading && documents.length === 0 ? ( | |
| <div className="text-center py-8"> | |
| <div className="w-8 h-8 border-2 border-primary border-t-transparent rounded-full animate-spin mx-auto mb-2" /> | |
| <p className="text-sm text-muted-foreground"> | |
| Loading context documents... | |
| </p> | |
| </div> | |
| ) : documents.length === 0 ? ( | |
| <div className="text-center py-8 text-muted-foreground"> | |
| <FileText className="h-12 w-12 mx-auto mb-3 opacity-50" /> | |
| <p className="font-medium mb-2">No context documents yet</p> | |
| <p className="text-sm mb-4"> | |
| Add domain knowledge, schemas, or guidelines to improve extraction | |
| quality. | |
| </p> | |
| <Button | |
| onClick={handleAddContext} | |
| size="sm" | |
| data-context-add-button | |
| > | |
| <Plus className="h-4 w-4 mr-2" /> | |
| Add Your First Context Document | |
| </Button> | |
| </div> | |
| ) : ( | |
| <div className="space-y-3"> | |
| {documents.map((document) => ( | |
| <ContextDocumentCard | |
| key={document.id} | |
| document={document} | |
| onDelete={() => handleDeleteDocument(document.id)} | |
| onViewEdit={() => handleViewEdit(document)} | |
| /> | |
| ))} | |
| </div> | |
| )} | |
| </div> | |
| {/* Upload Dialog */} | |
| <ContextUploadDialog | |
| isOpen={isUploadDialogOpen} | |
| onClose={() => setIsUploadDialogOpen(false)} | |
| traceId={traceId} | |
| onSuccess={() => { | |
| setIsUploadDialogOpen(false); | |
| loadDocuments(traceId); | |
| }} | |
| /> | |
| {/* Context Document Modal */} | |
| {selectedDocument && ( | |
| <ContextDocumentModal | |
| document={selectedDocument} | |
| isOpen={isModalOpen} | |
| onClose={handleModalClose} | |
| onSave={handleDocumentSave} | |
| onDelete={handleDocumentDelete} | |
| /> | |
| )} | |
| </> | |
| ); | |
| } | |