Add Cortex Portal — web visualization for knowledge graph
Express API server wrapping existing store/graph core with REST endpoints for nodes, edges, graph, and search. React + Vite portal with React Flow for interactive graph visualization, Tailwind CSS styling, and full CRUD UI (sidebar, node panel, add/link modals, search bar, toast notifications).
This commit is contained in:
62
portal/src/App.tsx
Normal file
62
portal/src/App.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import GraphView from './components/GraphView';
|
||||
import Sidebar from './components/Sidebar';
|
||||
import NodePanel from './components/NodePanel';
|
||||
import AddNodeModal from './components/AddNodeModal';
|
||||
import LinkModal from './components/LinkModal';
|
||||
import Toast from './components/Toast';
|
||||
|
||||
export default function App() {
|
||||
const [selectedId, setSelectedId] = useState<string | null>(null);
|
||||
const [showAddNode, setShowAddNode] = useState(false);
|
||||
const [linkFromId, setLinkFromId] = useState<string | null>(null);
|
||||
const [toast, setToast] = useState<string | null>(null);
|
||||
const qc = useQueryClient();
|
||||
|
||||
const refresh = useCallback(() => {
|
||||
qc.invalidateQueries({ queryKey: ['graph'] });
|
||||
qc.invalidateQueries({ queryKey: ['nodes'] });
|
||||
}, [qc]);
|
||||
|
||||
const notify = useCallback((msg: string) => {
|
||||
setToast(msg);
|
||||
setTimeout(() => setToast(null), 3000);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="flex h-screen w-screen overflow-hidden">
|
||||
<Sidebar
|
||||
selectedId={selectedId}
|
||||
onSelect={setSelectedId}
|
||||
onAddNode={() => setShowAddNode(true)}
|
||||
/>
|
||||
<div className="flex-1 relative">
|
||||
<GraphView selectedId={selectedId} onSelect={setSelectedId} />
|
||||
</div>
|
||||
{selectedId && (
|
||||
<NodePanel
|
||||
nodeId={selectedId}
|
||||
onClose={() => setSelectedId(null)}
|
||||
onLink={(id) => setLinkFromId(id)}
|
||||
onRefresh={refresh}
|
||||
onNotify={notify}
|
||||
/>
|
||||
)}
|
||||
{showAddNode && (
|
||||
<AddNodeModal
|
||||
onClose={() => setShowAddNode(false)}
|
||||
onCreated={() => { refresh(); notify('Node created'); }}
|
||||
/>
|
||||
)}
|
||||
{linkFromId && (
|
||||
<LinkModal
|
||||
fromId={linkFromId}
|
||||
onClose={() => setLinkFromId(null)}
|
||||
onCreated={() => { refresh(); notify('Edge created'); }}
|
||||
/>
|
||||
)}
|
||||
{toast && <Toast message={toast} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user