feat: implement snapshot restore functionality and enhance graph state management
All checks were successful
On Push to Main / test (push) Successful in 40s
On Push to Main / publish (push) Successful in 1m10s
On Push to Main / deploy-dev (push) Successful in 1m35s

This commit is contained in:
RaymondVerhoef
2026-05-27 17:43:18 +02:00
parent 6ea8860b96
commit 6309ae716b
4 changed files with 164 additions and 11 deletions

View File

@@ -1,10 +1,10 @@
import { RefreshCw, AlertCircle } from 'lucide-react';
import { RotateCcw, RefreshCw, AlertCircle } from 'lucide-react';
import Button from '../../ui/Button';
import SuggestionsQueue from '../SuggestionsQueue';
/**
* Sidebar controls panel: the exclude-nodes toggle, the AI analysis button,
* and the R42 suggestions queue.
* the snapshot restore row, and the R42 suggestions queue.
*
* Kept intentionally thin — all async logic lives in the KnowledgeGraph
* orchestrator and is injected via props.
@@ -17,7 +17,14 @@ export default function GraphControls({
analyzeError,
disabled,
onApplied,
snapshotMeta,
onRestore,
isRestoring,
}) {
const snapshotLabel = snapshotMeta
? new Date(snapshotMeta.ts).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
: null;
return (
<div className="mb-6 pb-4 border-b border-bg-warm space-y-3">
<label className="flex items-center gap-2 text-sm text-fg-muted cursor-pointer">
@@ -30,18 +37,33 @@ export default function GraphControls({
Show Excluded Nodes (Reference Material)
</label>
<div>
<div className="space-y-2">
<Button
onClick={onAnalyze}
disabled={isAnalyzing || disabled}
disabled={isAnalyzing || isRestoring || disabled}
className="w-full flex justify-center items-center gap-2"
>
<RefreshCw size={16} className={isAnalyzing ? 'animate-spin' : ''} />
{isAnalyzing ? 'Analyzing Graph…' : 'Analyze & Optimize Graph'}
</Button>
{/* Restore row — only visible after a bulkSave has written a snapshot */}
{snapshotMeta && (
<button
onClick={onRestore}
disabled={isAnalyzing || isRestoring}
className="w-full flex items-center justify-center gap-1.5 text-xs text-fg-muted hover:text-teal disabled:opacity-40 disabled:cursor-not-allowed py-1 transition-colors"
title={`Restore graph to state from ${snapshotLabel} (${snapshotMeta.topicCount} topics, ${snapshotMeta.relationCount} relations)`}
>
<RotateCcw size={12} className={isRestoring ? 'animate-spin' : ''} />
{isRestoring
? 'Restoring…'
: `Restore to ${snapshotLabel} · ${snapshotMeta.topicCount}t ${snapshotMeta.relationCount}r`}
</button>
)}
{analyzeError && (
<p className="mt-2 text-xs text-red-600 flex items-start gap-1">
<p className="text-xs text-red-600 flex items-start gap-1">
<AlertCircle size={14} className="shrink-0 mt-0.5" />
{analyzeError}
</p>