import React, { useState } from 'react'; import { FileDiff, AlertTriangle, CheckCircle2, XCircle, Archive, Layers, Sparkles, Quote, Eye, Columns, AlignLeft, ChevronRight, MessageSquare, Send, SlidersHorizontal, ShieldCheck } from 'lucide-react'; import confetti from 'canvas-confetti'; export default function SemanticDiffViewer({ diffData, onReviewProposal, onBatchReview, loading }) { const [viewMode, setViewMode] = useState('split'); // 'split' | 'unified' const [filterType, setFilterType] = useState('all'); // 'all' | 'conflicts' | 'additions' | 'changes' const [comments, setComments] = useState({}); const [expandedComments, setExpandedComments] = useState({}); const [actionLoading, setActionLoading] = useState({}); const [reviewedLocal, setReviewedLocal] = useState({}); const [lastActionToast, setLastActionToast] = useState(null); if (loading) { return (

Running semantic fact diff against Master Knowledge Register...

); } const rawAdditions = diffData?.additions || []; const rawChanges = diffData?.changes || []; const rawConflicts = diffData?.conflicts || []; const rawRemovals = diffData?.removals || []; // Filter out any delta that was just approved or rejected in local state const additions = rawAdditions.filter((a) => !reviewedLocal[a.proposal_id] && !reviewedLocal[a.id]); const changes = rawChanges.filter((c) => !reviewedLocal[c.proposal_id] && !reviewedLocal[c.id]); const conflicts = rawConflicts.filter((c) => !reviewedLocal[c.proposal_id] && !reviewedLocal[c.id]); const removals = rawRemovals.filter((r) => !reviewedLocal[r.proposal_id] && !reviewedLocal[r.id]); const totalDeltas = additions.length + changes.length + conflicts.length + removals.length; const handleAction = async (proposalId, decision) => { const allDeltas = [...rawConflicts, ...rawAdditions, ...rawChanges, ...rawRemovals]; const found = allDeltas.find((d) => (d.proposal_id === proposalId || d.id === proposalId)); const title = found?.title || found?.proposed_value || found?.summary || `Delta #${proposalId.slice(0, 8)}`; // Instant local removal & feedback banner setReviewedLocal((prev) => ({ ...prev, [proposalId]: { decision, title, time: new Date().toLocaleTimeString() } })); setLastActionToast({ proposalId, decision, title, time: new Date().toLocaleTimeString() }); setActionLoading((prev) => ({ ...prev, [proposalId]: true })); try { if (decision === 'APPROVED') { confetti({ particleCount: 50, spread: 60, origin: { y: 0.8 }, colors: ['#10B981', '#14B8A6', '#3B82F6'], }); } await onReviewProposal(proposalId, decision, comments[proposalId] || ''); } catch (err) { console.error('Error applying diff review:', err); } finally { setActionLoading((prev) => ({ ...prev, [proposalId]: false })); } }; const handleBatch = async (decision) => { const allIds = [...rawConflicts, ...rawAdditions, ...rawChanges, ...rawRemovals].map((d) => d.proposal_id || d.id); const batchMap = {}; allIds.forEach((id) => { batchMap[id] = { decision, title: 'Batch Knowledge Item' }; }); setReviewedLocal((prev) => ({ ...prev, ...batchMap })); setLastActionToast({ decision, title: `Batch of ${allIds.length} semantic fact deltas`, time: new Date().toLocaleTimeString() }); if (decision === 'APPROVED') { confetti({ particleCount: 90, spread: 80, origin: { y: 0.7 }, }); } await onBatchReview(decision); }; return (
{/* Diff Toolbar & Delta Summary */}
3-Way Semantic Fact Diff {totalDeltas} pending {totalDeltas === 1 ? 'delta' : 'deltas'}
+{additions.length} added ~{changes.length} updated {conflicts.length > 0 && ( !{conflicts.length} conflicts )}
{/* View Controls & Filters */}
{/* Filter Pills */}
{/* Split / Unified Toggle */}
{/* Bulk Approve Button */} {totalDeltas > 0 && ( )}
{/* Reactive Recent Action Toast Banner */} {lastActionToast && (
{lastActionToast.decision === 'APPROVED' ? : }
{lastActionToast.decision === 'APPROVED' ? '✓ Approved & Merged to Master Truth Register' : '✕ Rejected & Dismissed'}: {lastActionToast.proposalId && ( ID: {lastActionToast.proposalId.slice(0, 8)} )} at {lastActionToast.time}

"{lastActionToast.title}"

)} {totalDeltas === 0 ? (

Knowledge Register is Up to Date

No pending knowledge proposals or conflicts found. Stage a new document or protocol version to generate 3-way fact diffs.

) : (
{/* Conflicts Section */} {(filterType === 'all' || filterType === 'conflicts') && conflicts.map((c) => (
{/* Card Header */}
{c.title || 'Fact Conflict'} Conflict
ID: {c.proposal_id.slice(0, 8)}
{/* Conflict Body */}
{c.rationale && (

Reconciliation Note: {c.rationale}

)} {/* Side by Side Diff Comparison */}
{/* Baseline / Existing */}
- Master Truth (Existing Fact) Conf: {Math.round((c.existing?.confidence || 0.9) * 100)}%
{c.existing?.value || 'N/A'}
{/* Proposed / Amendment */}
+ Proposed Fact (Extracted Amendment) Conf: {Math.round((c.proposed?.confidence || 0.95) * 100)}%
{c.proposed?.value || 'N/A'}
{/* Evidence Provenance */} {c.proposed?.evidence && c.proposed.evidence.length > 0 && (
Source Provenance Citation {c.proposed.evidence[0]}
)} {/* Action Bar */}
{/* Reviewer Note Drawer */} {expandedComments[c.proposal_id] && (
setComments({ ...comments, [c.proposal_id]: e.target.value })} className="w-full bg-[#161F32] border border-slate-700 text-xs text-slate-200 px-3 py-2 rounded-lg focus:outline-none focus:ring-1 focus:ring-emerald-500" />
)}
))} {/* Additions Section */} {(filterType === 'all' || filterType === 'additions') && additions.map((a) => (
{/* Card Header */}
{a.title || 'New Knowledge Delta'} +{a.type || 'CLAIM'}
{Math.round((a.confidence || 0.95) * 100)}% Confidence
{/* Card Body */}
{a.proposed_value || a.summary || 'Extracted content'}
{a.evidence && a.evidence.length > 0 && (
Document Citation {a.evidence[0]}
)} {/* Action Bar */}
))} {/* Changes / Updates Section */} {(filterType === 'all' || filterType === 'changes') && changes.map((ch) => (
{ch.title || 'Knowledge Update'} UPDATE
- Previous Value
{ch.existing?.value || 'N/A'}
+ Updated Value
{ch.proposed?.value || 'N/A'}
))}
)}
); }