feat: knowledge graph table view met sort, group en bulk edit
All checks were successful
On Pull Request to Main / test (pull_request) Successful in 48s
On Pull Request to Main / publish (pull_request) Successful in 1m20s
On Pull Request to Main / deploy-dev (pull_request) Successful in 1m52s

Voegt een Graph/Table toggle toe aan de admin Knowledge Graph. De tabel
ondersteunt sorteren per kolom, groeperen op type/relevance/theme/difficulty,
filteren op label, multi-select en bulk-wijzigen van type, learning_relevance
en relevance_locked.

Closes #10

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
RaymondVerhoef
2026-06-03 17:08:51 +02:00
parent 218f6e7d64
commit eb08c4ad96
3 changed files with 540 additions and 11 deletions

View File

@@ -3,9 +3,11 @@ import * as d3 from 'd3';
import * as db from '../../lib/db';
import { callLLM } from '../../lib/llm';
import { EMIT_GRAPH_ACTIONS_TOOL } from '../../lib/llmTools';
import { Network, Table2 } from 'lucide-react';
import { useGraphData } from '../../hooks/useGraphData';
import GraphControls from './graph/GraphControls';
import NodeDetailPanel from './graph/NodeDetailPanel';
import GraphTable from './graph/GraphTable';
// ── Edge visual style per relation type ────────────────────────────────────────
// stroke — line colour
@@ -72,10 +74,11 @@ const KnowledgeGraph = () => {
const [isAnalyzing, setIsAnalyzing] = useState(false);
const [analyzeError, setAnalyzeError] = useState(null);
const [isRestoring, setIsRestoring] = useState(false);
const [viewMode, setViewMode] = useState('graph'); // 'graph' | 'table'
const {
topics, relations, snapshotMeta,
reload, updateTopic, deleteTopic, addRelation, removeRelation, bulkSave, restoreSnapshot,
reload, updateTopic, bulkUpdateTopics, deleteTopic, addRelation, removeRelation, bulkSave, restoreSnapshot,
} = useGraphData();
// ── Canvas sizing ────────────────────────────────────────────────────────────
@@ -94,6 +97,7 @@ const KnowledgeGraph = () => {
// ── D3 force graph ───────────────────────────────────────────────────────────
useEffect(() => {
if (viewMode !== 'graph') return;
if (!svgRef.current || topics.length === 0) return;
const { width, height } = dimensions;
@@ -236,7 +240,7 @@ const KnowledgeGraph = () => {
});
return () => simulation.stop();
}, [dimensions, topics, relations, showExcludeNodes]);
}, [dimensions, topics, relations, showExcludeNodes, viewMode]);
// ── Pan/zoom canvas to a node ────────────────────────────────────────────────
@@ -396,17 +400,58 @@ const KnowledgeGraph = () => {
return (
<div className="relative w-full h-full flex flex-col md:flex-row">
{/* D3 canvas */}
<div
ref={wrapperRef}
className="flex-1 h-[400px] md:h-full cursor-grab active:cursor-grabbing border-r border-bg-warm"
>
{topics.length === 0 ? (
<div className="h-full flex items-center justify-center text-fg-muted p-8 text-center">
No knowledge graph data yet. Upload source material in the Sources tab first.
{/* Main view: graph canvas or table */}
<div className="flex-1 flex flex-col border-r border-bg-warm min-w-0">
{/* View-mode toggle */}
<div className="flex items-center gap-1 px-3 py-2 border-b border-bg-warm bg-paper">
<button
onClick={() => setViewMode('graph')}
className={`flex items-center gap-1.5 px-3 py-1 text-xs rounded-[var(--r-sm)] transition-colors ${
viewMode === 'graph' ? 'bg-bg-warm text-teal font-medium' : 'text-fg-muted hover:bg-bg-warm/50'
}`}
title="Force-directed graph view"
>
<Network size={13} /> Graph
</button>
<button
onClick={() => setViewMode('table')}
className={`flex items-center gap-1.5 px-3 py-1 text-xs rounded-[var(--r-sm)] transition-colors ${
viewMode === 'table' ? 'bg-bg-warm text-teal font-medium' : 'text-fg-muted hover:bg-bg-warm/50'
}`}
title="Sortable table with bulk edit"
>
<Table2 size={13} /> Table
</button>
</div>
{viewMode === 'graph' ? (
<div
ref={wrapperRef}
className="flex-1 h-[400px] md:h-full cursor-grab active:cursor-grabbing"
>
{topics.length === 0 ? (
<div className="h-full flex items-center justify-center text-fg-muted p-8 text-center">
No knowledge graph data yet. Upload source material in the Sources tab first.
</div>
) : (
<svg ref={svgRef} className="w-full h-full" />
)}
</div>
) : (
<svg ref={svgRef} className="w-full h-full" />
<div className="flex-1 min-h-[400px] overflow-hidden">
{topics.length === 0 ? (
<div className="h-full flex items-center justify-center text-fg-muted p-8 text-center">
No knowledge graph data yet. Upload source material in the Sources tab first.
</div>
) : (
<GraphTable
topics={topics}
relations={relations}
onBulkUpdate={bulkUpdateTopics}
onSelectNode={setSelectedNode}
/>
)}
</div>
)}
</div>