From 190d1a6e0b30e63ccf4b50e9144d4e7eecc7cf7e Mon Sep 17 00:00:00 2001 From: RaymondVerhoef Date: Mon, 18 May 2026 22:12:19 +0200 Subject: [PATCH] feat: add KnowledgeGraph component for visualizing, editing, and syncing handbook content with AI-driven analysis --- src/components/admin/KnowledgeGraph.jsx | 37 ++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/src/components/admin/KnowledgeGraph.jsx b/src/components/admin/KnowledgeGraph.jsx index bf992c5..543fae3 100644 --- a/src/components/admin/KnowledgeGraph.jsx +++ b/src/components/admin/KnowledgeGraph.jsx @@ -28,8 +28,19 @@ const KnowledgeGraph = () => { const [relations, setRelations] = useState([]); const reloadKb = useCallback(() => { - db.getTopics().then(setTopics); - db.getRelations().then(setRelations); + Promise.all([db.getTopics(), db.getRelations()]).then(([allTopics, allRelations]) => { + const topicIds = new Set(allTopics.map(t => t.id)); + const validRelations = allRelations + .map(r => ({ + ...r, + source: r.source?.id || r.source, + target: r.target?.id || r.target + })) + .filter(r => topicIds.has(r.source) && topicIds.has(r.target)); + + setTopics(allTopics); + setRelations(validRelations); + }); }, []); useEffect(() => { @@ -164,7 +175,13 @@ const KnowledgeGraph = () => { const deleteNode = async () => { if (confirm(`Are you sure you want to delete "${selectedNode.label}"? This will also remove any relations connected to it.`)) { const updatedTopics = topics.filter(t => t.id !== selectedNode.id); - const updatedRelations = relations.filter(r => r.source !== selectedNode.id && r.target !== selectedNode.id); + const updatedRelations = relations + .map(r => ({ + ...r, + source: r.source?.id || r.source, + target: r.target?.id || r.target + })) + .filter(r => r.source !== selectedNode.id && r.target !== selectedNode.id); await db.saveTopics(updatedTopics); await db.saveRelations(updatedRelations); @@ -324,7 +341,19 @@ Analyze this graph and return ONLY the optimized JSON object with this EXACT str } } - updatedRelations = updatedRelations.filter(r => r.source !== r.target); + // Ensure all relations only reference existing nodes and are normalized to string IDs + const finalTopicIds = new Set(updatedTopics.map(t => t.id)); + updatedRelations = updatedRelations + .map(r => ({ + ...r, + source: r.source?.id || r.source, + target: r.target?.id || r.target + })) + .filter(r => + r.source !== r.target && + finalTopicIds.has(r.source) && + finalTopicIds.has(r.target) + ); await db.saveTopics(updatedTopics); await db.saveRelations(updatedRelations);