@@ -22,6 +22,7 @@ import { Badge } from "@/components/ui/badge";
2222import { Switch } from "@/components/ui/switch" ;
2323import { Skeleton } from "@/components/ui/skeleton" ;
2424import { showAlert } from "@/components/ui/alert-system" ;
25+ import { MultiSelect } from "@/components/ui/multi-select" ;
2526
2627// Dynamically import ForceGraphComponent to avoid SSR issues
2728const ForceGraphComponent = dynamic ( ( ) => import ( "@/components/ForceGraphComponent" ) , {
@@ -109,6 +110,14 @@ const entityTypeColors: Record<string, string> = {
109110
110111const POLL_INTERVAL_MS = 60000 ; // 1 minute
111112
113+ // Interface for document API response
114+ interface ApiDocumentResponse {
115+ external_id ?: string ;
116+ id ?: string ;
117+ filename ?: string ;
118+ name ?: string ;
119+ }
120+
112121const GraphSection : React . FC < GraphSectionProps > = ( {
113122 apiBaseUrl,
114123 onSelectGraph,
@@ -154,6 +163,10 @@ const GraphSection: React.FC<GraphSectionProps> = ({
154163 const [ selectedNode , setSelectedNode ] = useState < NodeObject | null > ( null ) ;
155164 const [ sidebarOpen , setSidebarOpen ] = useState ( false ) ;
156165
166+ // Document selection state
167+ const [ documents , setDocuments ] = useState < { id : string ; filename : string } [ ] > ( [ ] ) ;
168+ const [ loadingDocuments , setLoadingDocuments ] = useState ( false ) ;
169+
157170 // Refs for graph visualization
158171 const graphContainerRef = useRef < HTMLDivElement > ( null ) ;
159172 // Removed graphInstance ref as it's not needed with the dynamic component
@@ -290,10 +303,61 @@ const GraphSection: React.FC<GraphSectionProps> = ({
290303 }
291304 } , [ apiBaseUrl , createHeaders ] ) ;
292305
306+ // Fetch documents
307+ const fetchDocuments = useCallback ( async ( ) => {
308+ if ( ! apiBaseUrl ) return ;
309+
310+ setLoadingDocuments ( true ) ;
311+ try {
312+ console . log ( `Fetching documents from: ${ apiBaseUrl } /documents` ) ;
313+ const headers = createHeaders ( "application/json" ) ;
314+ const response = await fetch ( `${ apiBaseUrl } /documents` , {
315+ method : "POST" ,
316+ headers,
317+ body : JSON . stringify ( { } ) , // Empty body to fetch all docs
318+ } ) ;
319+
320+ if ( ! response . ok ) {
321+ throw new Error ( `Failed to fetch documents: ${ response . status } ${ response . statusText } ` ) ;
322+ }
323+
324+ const documentsData = await response . json ( ) ;
325+ console . log ( "Documents data received:" , documentsData ) ;
326+
327+ if ( Array . isArray ( documentsData ) ) {
328+ // Transform documents to the format we need (id and filename)
329+ const transformedDocs = documentsData
330+ . map ( ( doc : ApiDocumentResponse ) => {
331+ const id = doc . external_id || doc . id ;
332+ if ( ! id ) return null ; // Skip documents without valid IDs
333+
334+ return {
335+ id,
336+ filename : doc . filename || doc . name || `Document ${ id } ` ,
337+ } ;
338+ } )
339+ . filter ( ( doc ) : doc is { id : string ; filename : string } => doc !== null ) ;
340+
341+ setDocuments ( transformedDocs ) ;
342+ } else {
343+ console . error ( "Expected array for documents data but received:" , typeof documentsData ) ;
344+ }
345+ } catch ( err ) {
346+ console . error ( "Error fetching documents:" , err ) ;
347+ } finally {
348+ setLoadingDocuments ( false ) ;
349+ }
350+ } , [ apiBaseUrl , createHeaders ] ) ;
351+
293352 // Fetch graphs on component mount
294353 useEffect ( ( ) => {
295354 fetchGraphs ( ) ;
296- } , [ fetchGraphs ] ) ;
355+ // Also fetch documents when component mounts
356+ if ( authToken || apiBaseUrl . includes ( "localhost" ) ) {
357+ console . log ( "GraphSection: Fetching documents with auth token:" , ! ! authToken ) ;
358+ fetchDocuments ( ) ;
359+ }
360+ } , [ fetchGraphs , fetchDocuments , authToken , apiBaseUrl ] ) ;
297361
298362 // Fetch a specific graph
299363 const fetchGraph = useCallback (
@@ -681,26 +745,35 @@ const GraphSection: React.FC<GraphSectionProps> = ({
681745 < h3 className = "text-md mb-3 font-medium" > Document Selection</ h3 >
682746 < div className = "space-y-4" >
683747 < div className = "space-y-2" >
684- < Label htmlFor = "graph-documents" > Document IDs (Optional)</ Label >
685- < Textarea
686- id = "graph-documents"
687- placeholder = "Enter document IDs separated by commas"
688- value = { graphDocuments . join ( ", " ) }
689- onChange = { e =>
690- setGraphDocuments (
691- e . target . value
692- . split ( "," )
693- . map ( id => id . trim ( ) )
694- . filter ( id => id )
695- )
696- }
697- className = "min-h-[80px]"
748+ < Label htmlFor = "graph-documents" > Documents</ Label >
749+ < MultiSelect
750+ options = { [
751+ { label : "All Documents" , value : "__none__" } ,
752+ ...( loadingDocuments ? [ { label : "Loading documents..." , value : "loading" } ] : [ ] ) ,
753+ ...documents . map ( doc => ( {
754+ label : doc . filename ,
755+ value : doc . id ,
756+ } ) ) ,
757+ ] }
758+ selected = { graphDocuments }
759+ onChange = { ( value : string [ ] ) => {
760+ const filteredValues = value . filter ( v => v !== "__none__" ) ;
761+ setGraphDocuments ( filteredValues ) ;
762+ } }
763+ placeholder = "Select documents for the graph"
764+ className = "w-full"
698765 />
699766 < p className = "text-xs text-muted-foreground" >
700- Specify document IDs to include in the graph, or leave empty and use filters below.
767+ Select specific documents to include in the graph, or leave empty and use filters below.
701768 </ p >
702769 </ div >
703770
771+ < div className = "relative flex items-center" >
772+ < div className = "flex-grow border-t border-muted" > </ div >
773+ < span className = "mx-4 flex-shrink text-xs uppercase text-muted-foreground" > Or</ span >
774+ < div className = "flex-grow border-t border-muted" > </ div >
775+ </ div >
776+
704777 < div className = "space-y-2" >
705778 < Label htmlFor = "graph-filters" > Metadata Filters (Optional)</ Label >
706779 < Textarea
@@ -998,23 +1071,26 @@ const GraphSection: React.FC<GraphSectionProps> = ({
9981071 </ div >
9991072 < div className = "space-y-4" >
10001073 < div className = "space-y-2" >
1001- < Label htmlFor = "additional-documents" > Additional Document IDs</ Label >
1002- < Textarea
1003- id = "additional-documents"
1004- placeholder = "Enter document IDs separated by commas"
1005- value = { additionalDocuments . join ( ", " ) }
1006- onChange = { e =>
1007- setAdditionalDocuments (
1008- e . target . value
1009- . split ( "," )
1010- . map ( id => id . trim ( ) )
1011- . filter ( id => id )
1012- )
1013- }
1014- className = "min-h-[80px]"
1074+ < Label htmlFor = "additional-documents" > Additional Documents</ Label >
1075+ < MultiSelect
1076+ options = { [
1077+ { label : "All Documents" , value : "__none__" } ,
1078+ ...( loadingDocuments ? [ { label : "Loading documents..." , value : "loading" } ] : [ ] ) ,
1079+ ...documents . map ( doc => ( {
1080+ label : doc . filename ,
1081+ value : doc . id ,
1082+ } ) ) ,
1083+ ] }
1084+ selected = { additionalDocuments }
1085+ onChange = { ( value : string [ ] ) => {
1086+ const filteredValues = value . filter ( v => v !== "__none__" ) ;
1087+ setAdditionalDocuments ( filteredValues ) ;
1088+ } }
1089+ placeholder = "Select additional documents"
1090+ className = "w-full"
10151091 />
10161092 < p className = "text-xs text-muted-foreground" >
1017- Specify additional document IDs to include in the graph.
1093+ Select additional documents to include in the graph.
10181094 </ p >
10191095 </ div >
10201096
@@ -1040,7 +1116,7 @@ const GraphSection: React.FC<GraphSectionProps> = ({
10401116 </ div >
10411117 < Button
10421118 onClick = { handleUpdateGraph }
1043- disabled = { loading || ( additionalDocuments . length === 0 && additionalFilters === "{}" ) } // Disable if no input
1119+ disabled = { loading || ( additionalDocuments . length === 0 && additionalFilters === "{}" ) }
10441120 className = "w-full"
10451121 >
10461122 { loading ? (
0 commit comments