wu981526092's picture
🚀 Deploy AgentGraph: Complete agent monitoring and knowledge graph system
c2ea5ed
import React, { useState, useEffect } from "react";
import { Card, CardContent } from "@/components/ui/card";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { GitCompare, BarChart3, Eye } from "lucide-react";
import { api } from "@/lib/api";
import { LoadingSpinner } from "@/components/shared/LoadingSpinner";
import { GraphSelector } from "./GraphSelector";
import { ComparisonResults } from "./ComparisonResults";
import { VisualComparison } from "./VisualComparison";
import { AvailableGraph, GraphComparisonResults } from "@/types";
export const GraphComparisonView: React.FC = () => {
const [activeTab, setActiveTab] = useState("selection");
const [availableGraphs, setAvailableGraphs] = useState<AvailableGraph[]>([]);
const [selectedGraph1, setSelectedGraph1] = useState<AvailableGraph | null>(
null
);
const [selectedGraph2, setSelectedGraph2] = useState<AvailableGraph | null>(
null
);
const [comparisonResults, setComparisonResults] =
useState<GraphComparisonResults | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
// Load available graphs on component mount
useEffect(() => {
loadAvailableGraphs();
}, []);
// Auto-refresh available graphs every 15 seconds
useEffect(() => {
const interval = setInterval(() => {
if (!isLoading) {
loadAvailableGraphs();
}
}, 15000); // 15 seconds
return () => clearInterval(interval);
}, [isLoading]);
const loadAvailableGraphs = async () => {
try {
setIsLoading(true);
setError(null);
const response = await api.graphComparison.listAvailableGraphs();
setAvailableGraphs(response.final_graphs);
} catch (err) {
setError(err instanceof Error ? err.message : "Failed to load graphs");
} finally {
setIsLoading(false);
}
};
const handleGraphSelection = (
graph1: AvailableGraph | null,
graph2: AvailableGraph | null
) => {
setSelectedGraph1(graph1);
setSelectedGraph2(graph2);
// Remove auto-navigation - let users control when to switch tabs
};
const handleCompareGraphs = async () => {
if (!selectedGraph1 || !selectedGraph2) return;
try {
setIsLoading(true);
setError(null);
const results = await api.graphComparison.compareGraphs(
selectedGraph1.id,
selectedGraph2.id,
{ similarity_threshold: 0.7, use_cache: true }
);
setComparisonResults(results);
setActiveTab("results");
} catch (err) {
setError(err instanceof Error ? err.message : "Failed to compare graphs");
} finally {
setIsLoading(false);
}
};
const canCompare = selectedGraph1 && selectedGraph2;
return (
<div className="flex flex-col h-screen bg-background">
{/* Error Display */}
{error && (
<div className="mx-4 mt-4 p-4 bg-destructive/10 border border-destructive/20 rounded-lg">
<p className="text-destructive text-sm">{error}</p>
</div>
)}
{/* Content */}
<div className="flex-1 overflow-hidden">
<Tabs
value={activeTab}
onValueChange={setActiveTab}
className="h-full flex flex-col"
>
<div className="border-b enhanced-tabs px-4">
<TabsList className="grid w-full grid-cols-3 h-10 bg-transparent p-1 gap-1">
<TabsTrigger
value="selection"
className="flex items-center gap-2 h-8 px-3 rounded-md text-sm font-medium transition-all duration-200 data-[state=active]:bg-white data-[state=active]:text-primary data-[state=active]:shadow-sm data-[state=active]:border data-[state=active]:border-primary/20 hover:bg-white/60 hover:text-foreground text-muted-foreground"
>
<GitCompare className="h-3.5 w-3.5" />
Selection
</TabsTrigger>
<TabsTrigger
value="results"
disabled={!comparisonResults}
className="flex items-center gap-2 h-8 px-3 rounded-md text-sm font-medium transition-all duration-200 data-[state=active]:bg-white data-[state=active]:text-primary data-[state=active]:shadow-sm data-[state=active]:border data-[state=active]:border-primary/20 hover:bg-white/60 hover:text-foreground text-muted-foreground disabled:opacity-50 disabled:cursor-not-allowed"
>
<BarChart3 className="h-3.5 w-3.5" />
Results
</TabsTrigger>
<TabsTrigger
value="visual"
disabled={!canCompare}
className="flex items-center gap-2 h-8 px-3 rounded-md text-sm font-medium transition-all duration-200 data-[state=active]:bg-white data-[state=active]:text-primary data-[state=active]:shadow-sm data-[state=active]:border data-[state=active]:border-primary/20 hover:bg-white/60 hover:text-foreground text-muted-foreground disabled:opacity-50 disabled:cursor-not-allowed"
>
<Eye className="h-3.5 w-3.5" />
Visual
</TabsTrigger>
</TabsList>
</div>
<div className="flex-1 overflow-y-auto">
<TabsContent value="selection" className="h-full m-0 p-4">
{isLoading && !availableGraphs.length ? (
<div className="flex items-center justify-center h-64">
<LoadingSpinner size="lg" />
</div>
) : (
<GraphSelector
availableGraphs={availableGraphs}
selectedGraph1={selectedGraph1}
selectedGraph2={selectedGraph2}
onSelectionChange={handleGraphSelection}
onCompareGraphs={handleCompareGraphs}
isLoading={isLoading}
/>
)}
</TabsContent>
<TabsContent value="results" className="h-full m-0 p-4">
{comparisonResults ? (
<ComparisonResults results={comparisonResults} />
) : (
<Card>
<CardContent className="p-8 text-center">
<p className="text-muted-foreground">
No comparison results available
</p>
</CardContent>
</Card>
)}
</TabsContent>
<TabsContent value="visual" className="h-full m-0">
{canCompare ? (
<VisualComparison
graph1={selectedGraph1!}
graph2={selectedGraph2!}
comparisonResults={comparisonResults}
/>
) : (
<Card className="m-4">
<CardContent className="p-8 text-center">
<p className="text-muted-foreground">
Select 2 graphs to view visual comparison
</p>
</CardContent>
</Card>
)}
</TabsContent>
</div>
</Tabs>
</div>
</div>
);
};