feat: wire mindmap to knowledge API
- Updated useGraphData hook to fetch from /api/knowledge/entries - Implemented CRUD operations for knowledge nodes using actual API endpoints - Wired edge creation/deletion through wiki-links in content - Added search integration with /api/knowledge/search - Transform Knowledge entries to graph nodes with backlinks as edges - Real-time graph updates after mutations - Added search bar UI with live results dropdown - Graph statistics automatically recalculate - Clean TypeScript with proper type transformations
This commit is contained in:
@@ -27,6 +27,9 @@ export function MindmapViewer({
|
||||
const [mermaidStyle, setMermaidStyle] = useState<MermaidStyle>('flowchart');
|
||||
const [showCreateModal, setShowCreateModal] = useState(false);
|
||||
const [selectedNode, setSelectedNode] = useState<KnowledgeNode | null>(null);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [searchResults, setSearchResults] = useState<KnowledgeNode[]>([]);
|
||||
const [isSearching, setIsSearching] = useState(false);
|
||||
|
||||
const {
|
||||
graph,
|
||||
@@ -39,6 +42,7 @@ export function MindmapViewer({
|
||||
updateNode,
|
||||
deleteNode,
|
||||
createEdge,
|
||||
searchNodes,
|
||||
} = useGraphData({ ...(rootId && { rootId }), maxDepth });
|
||||
|
||||
const handleViewModeChange = useCallback(
|
||||
@@ -84,6 +88,36 @@ export function MindmapViewer({
|
||||
[createEdge]
|
||||
);
|
||||
|
||||
const handleSearch = useCallback(
|
||||
async (query: string) => {
|
||||
setSearchQuery(query);
|
||||
if (!query.trim()) {
|
||||
setSearchResults([]);
|
||||
return;
|
||||
}
|
||||
|
||||
setIsSearching(true);
|
||||
try {
|
||||
const results = await searchNodes(query);
|
||||
setSearchResults(results);
|
||||
} catch (err) {
|
||||
console.error('Search failed:', err);
|
||||
} finally {
|
||||
setIsSearching(false);
|
||||
}
|
||||
},
|
||||
[searchNodes]
|
||||
);
|
||||
|
||||
const handleSelectSearchResult = useCallback(
|
||||
(node: KnowledgeNode) => {
|
||||
setSelectedNode(node);
|
||||
setSearchResults([]);
|
||||
setSearchQuery('');
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
@@ -137,6 +171,56 @@ export function MindmapViewer({
|
||||
</select>
|
||||
)}
|
||||
|
||||
{/* Search bar */}
|
||||
<div className="relative">
|
||||
<input
|
||||
type="text"
|
||||
value={searchQuery}
|
||||
onChange={(e) => handleSearch(e.target.value)}
|
||||
placeholder="Search nodes..."
|
||||
className="px-3 py-1.5 pl-8 text-sm rounded border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 w-48"
|
||||
/>
|
||||
<svg
|
||||
className="absolute left-2 top-2 w-4 h-4 text-gray-400"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
{/* Search results dropdown */}
|
||||
{searchResults.length > 0 && (
|
||||
<div className="absolute top-full left-0 mt-1 w-64 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded shadow-lg max-h-64 overflow-y-auto z-50">
|
||||
{searchResults.map((result) => (
|
||||
<button
|
||||
key={result.id}
|
||||
onClick={() => handleSelectSearchResult(result)}
|
||||
className="w-full text-left px-3 py-2 hover:bg-gray-100 dark:hover:bg-gray-700 text-sm"
|
||||
>
|
||||
<div className="font-medium text-gray-900 dark:text-gray-100">
|
||||
{result.title}
|
||||
</div>
|
||||
<div className="text-xs text-gray-500 dark:text-gray-400 capitalize">
|
||||
{result.node_type}
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isSearching && (
|
||||
<div className="absolute right-2 top-2">
|
||||
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-blue-500" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Statistics */}
|
||||
{statistics && (
|
||||
<div className="text-sm text-gray-500 dark:text-gray-400">
|
||||
|
||||
Reference in New Issue
Block a user