wu981526092's picture
Add Perfect Sample Data for HF Spaces Demo
b450ab8
import React, { useState, useEffect, useCallback, useMemo } from "react";
import { useDashboardData } from "@/hooks/useDashboardData";
import { DashboardVisualization } from "../dashboard/DashboardVisualization";
import { EntityRelationAnalysisModal } from "../dashboard/modals/EntityRelationAnalysisModal";
import { TinyTrendChart } from "@/components/shared/TinyTrendChart";
import { EntityRelationTreeChart } from "@/components/shared/EntityRelationTreeChart";
import {
generateTrendData,
calculateFailureCount,
} from "@/lib/trend-data-generator";
import { useAgentGraph } from "@/context/AgentGraphContext";
import { api } from "@/lib/api";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Skeleton } from "@/components/ui/skeleton";
import {
FileText,
AlertCircle,
Clock,
Users,
AlertTriangle,
} from "lucide-react";
export function DashboardView() {
const { state, actions } = useAgentGraph();
const { stats, recentActivity, isLoading, error, refresh } =
useDashboardData();
// Modal states
const [isEntityModalOpen, setIsEntityModalOpen] = useState(false);
// Generate realistic trend data based on current traces
const trendData = useMemo(
() => generateTrendData(state.traces),
[state.traces]
);
// Load traces data when dashboard mounts (if not already loaded)
const loadTracesData = useCallback(async () => {
if (state.traces.length === 0 && !state.isLoading) {
actions.setLoading(true);
try {
const tracesData = await api.traces.list();
actions.setTraces(Array.isArray(tracesData) ? tracesData : []);
} catch (error) {
actions.setError(
error instanceof Error ? error.message : "Failed to load traces"
);
actions.setTraces([]);
} finally {
actions.setLoading(false);
}
}
}, [state.traces.length, state.isLoading, actions]);
useEffect(() => {
loadTracesData();
}, [loadTracesData]);
const handleUploadTrace = () => {
actions.setActiveView("upload");
};
return (
<div className="flex-1 flex flex-col p-6 gap-6 min-h-0 relative">
{/* Glassmorphism background gradient */}
<div className="absolute inset-0 bg-gradient-to-br from-blue-50/30 via-purple-50/20 to-green-50/30 dark:from-blue-950/20 dark:via-purple-950/10 dark:to-green-950/20 pointer-events-none" />
{/* Content with relative positioning */}
<div className="relative z-10 flex flex-col gap-6 h-full">
{/* Error State */}
{error && (
<Card className="border-destructive/50 bg-destructive/10">
<CardContent className="p-4">
<div className="flex items-center gap-2 text-destructive">
<AlertCircle className="h-4 w-4" />
<span className="text-sm">{error}</span>
<Button
variant="outline"
size="sm"
onClick={refresh}
className="ml-auto"
>
Try Again
</Button>
</div>
</CardContent>
</Card>
)}
{/* Stats Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3">
{/* Traces & Agent Graphs - Blue & Green Theme */}
<div className="glass-card glass-card-blue rounded-2xl p-4 cursor-pointer group">
<div className="flex items-center justify-between mb-3">
<div className="flex items-center gap-3">
<div className="glass-icon rounded-xl p-2">
<FileText className="h-5 w-5 text-blue-500" />
</div>
<div>
<h3 className="text-sm font-medium text-foreground/90">
Traces & Graphs
</h3>
{isLoading ? (
<div className="flex gap-2">
<Skeleton className="h-6 w-8 mt-1" />
<span className="text-xs text-muted-foreground mt-2">
</span>
<Skeleton className="h-6 w-8 mt-1" />
</div>
) : (
<div className="flex items-center gap-2">
<div className="text-xl font-bold glass-number text-blue-600 dark:text-blue-400">
{stats.totalTraces}
</div>
<span className="text-xs text-muted-foreground"></span>
<div className="text-xl font-bold glass-number text-green-600 dark:text-green-400">
{stats.totalAgentGraphs}
</div>
</div>
)}
</div>
</div>
</div>
{/* Dual-line trend chart */}
<div className="mb-2">
<TinyTrendChart
data={trendData.tracesAndGraphs}
color="#3b82f6"
color2="#22c55e"
isDual={true}
/>
</div>
{stats.totalTraces > 0 ? (
<p className="text-xs text-muted-foreground">
<span className="text-blue-600">Traces</span> ready •{" "}
<span className="text-green-600">Graphs</span> generated
</p>
) : (
<Button
variant="link"
size="sm"
onClick={handleUploadTrace}
className="text-xs text-blue-600 hover:text-blue-500 p-0 h-auto font-normal"
>
Upload your first trace
</Button>
)}
</div>
{/* Entities & Relations - Purple & Orange Theme */}
<div
className="glass-card glass-card-purple rounded-2xl p-4 cursor-pointer group hover:scale-105 transition-all duration-300"
onClick={() => setIsEntityModalOpen(true)}
>
<div className="flex items-center justify-between mb-3">
<div className="flex items-center gap-3">
<div className="glass-icon rounded-xl p-2">
<Users className="h-5 w-5 text-purple-500" />
</div>
<div>
<h3 className="text-sm font-medium text-foreground/90">
Entities & Relations
</h3>
{isLoading ? (
<div className="flex gap-2">
<Skeleton className="h-6 w-12 mt-1" />
<span className="text-xs text-muted-foreground mt-2">
</span>
<Skeleton className="h-6 w-12 mt-1" />
</div>
) : (
<div className="flex items-center gap-2">
<div className="text-xl font-bold glass-number text-purple-600 dark:text-purple-400">
{stats.totalEntities}
</div>
<span className="text-xs text-muted-foreground"></span>
<div className="text-xl font-bold glass-number text-orange-600 dark:text-orange-400">
{stats.totalRelations}
</div>
</div>
)}
</div>
</div>
</div>
{/* Entity-Relation Tree Visualization */}
<div className="mb-2">
<EntityRelationTreeChart />
</div>
<p className="text-xs text-muted-foreground">
<span className="text-purple-600">Entities</span> extracted •{" "}
<span className="text-orange-600">Relations</span> connected
</p>
</div>
{/* Failures - Red Theme */}
<div className="glass-card-red rounded-2xl p-4 cursor-pointer group backdrop-blur-md">
<div className="flex items-center justify-between mb-3">
<div className="flex items-center gap-3">
<div className="glass-icon rounded-xl p-2 bg-red-500/10">
<AlertTriangle className="h-5 w-5 text-red-500" />
</div>
<div>
<h3 className="text-sm font-medium text-foreground/90">
System Failures
</h3>
{isLoading ? (
<Skeleton className="h-6 w-12 mt-1" />
) : (
<div className="text-xl font-bold glass-number text-red-600 dark:text-red-400">
{calculateFailureCount(state.traces)}
</div>
)}
</div>
</div>
</div>
{/* Failures trend chart */}
<div className="mb-2">
<TinyTrendChart data={trendData.failures} color="#dc2626" />
</div>
<p className="text-xs text-muted-foreground">
Processing errors tracked
</p>
</div>
</div>
{/* Dashboard Visualization */}
<DashboardVisualization className="mt-6" />
{/* Main Content Area - Recent Activity */}
<div className="flex-1 flex flex-col gap-6 min-h-0 overflow-auto">
{/* Recent Activity - Now takes full available space */}
<Card className="flex-1 flex flex-col min-h-0">
<CardHeader className="pb-4">
<CardTitle className="flex items-center gap-2">
<Clock className="h-4 w-4" />
Recent Activity
</CardTitle>
<CardDescription>
Latest system events and processing updates
</CardDescription>
</CardHeader>
<CardContent className="flex-1 overflow-auto">
{isLoading ? (
<div className="space-y-4">
{[...Array(3)].map((_, i) => (
<div key={i} className="flex items-center justify-between">
<div className="space-y-2">
<Skeleton className="h-4 w-32" />
<Skeleton className="h-3 w-24" />
</div>
<Skeleton className="h-6 w-16" />
</div>
))}
</div>
) : recentActivity.length > 0 ? (
<div className="space-y-4">
{recentActivity.map((activity) => (
<div
key={activity.id}
className="flex items-center justify-between"
>
<div className="space-y-1">
<p className="text-sm font-medium">{activity.action}</p>
<p className="text-xs text-muted-foreground">
{activity.trace}
</p>
</div>
<div className="text-right space-y-1">
<Badge
variant={
activity.status === "success"
? "default"
: activity.status === "error"
? "destructive"
: activity.status === "processing"
? "secondary"
: "secondary"
}
className={`text-xs ${
activity.status === "success"
? "bg-green-100 text-green-800 border-green-200 dark:bg-green-900 dark:text-green-300"
: activity.status === "error"
? "bg-red-100 text-red-800 border-red-200 dark:bg-red-900 dark:text-red-300"
: activity.status === "processing"
? "bg-blue-100 text-blue-800 border-blue-200 dark:bg-blue-900 dark:text-blue-300"
: "bg-gray-100 text-gray-800 border-gray-200 dark:bg-gray-900 dark:text-gray-300"
}`}
>
{activity.status}
</Badge>
<p className="text-xs text-muted-foreground">
{activity.timestamp}
</p>
</div>
</div>
))}
</div>
) : (
<div className="text-center py-8">
<Clock className="h-8 w-8 mx-auto mb-2 text-muted-foreground" />
<p className="text-sm text-muted-foreground">
No recent activity
</p>
</div>
)}
</CardContent>
</Card>
</div>
{/* Analysis Modals */}
<EntityRelationAnalysisModal
open={isEntityModalOpen}
onOpenChange={setIsEntityModalOpen}
/>
</div>
</div>
);
}