Skip to content

Commit 32b114f

Browse files
committed
Direct doc selection in graphs
1 parent af5783f commit 32b114f

2 files changed

Lines changed: 109 additions & 33 deletions

File tree

ee/ui-component/components/GraphSection.tsx

Lines changed: 108 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { Badge } from "@/components/ui/badge";
2222
import { Switch } from "@/components/ui/switch";
2323
import { Skeleton } from "@/components/ui/skeleton";
2424
import { showAlert } from "@/components/ui/alert-system";
25+
import { MultiSelect } from "@/components/ui/multi-select";
2526

2627
// Dynamically import ForceGraphComponent to avoid SSR issues
2728
const ForceGraphComponent = dynamic(() => import("@/components/ForceGraphComponent"), {
@@ -109,6 +110,14 @@ const entityTypeColors: Record<string, string> = {
109110

110111
const 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+
112121
const 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 ? (

ee/ui-component/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@morphik/ui",
3-
"version": "0.2.28",
3+
"version": "0.2.29",
44
"private": true,
55
"description": "Modern UI component for Morphik - A powerful document processing and querying system",
66
"author": "Morphik Team",

0 commit comments

Comments
 (0)