import { useState, useEffect, useCallback, useMemo } from "react"; import { useAgentGraph } from "@/context/AgentGraphContext"; interface DashboardStats { totalTraces: number; totalAgentGraphs: number; totalEntities: number; totalRelations: number; completedProcessing: number; processingProgress: number; systemStatus: "healthy" | "warning" | "error"; } interface ActivityItem { id: string; action: string; trace: string; timestamp: string; status: "success" | "error" | "processing"; } export function useDashboardData() { const { state } = useAgentGraph(); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); // Calculate stats using useMemo to avoid unnecessary recalculations const stats = useMemo(() => { const totalTraces = state.traces.length; const totalAgentGraphs = state.traces.reduce((total, trace) => { // Count only final knowledge graphs (consistent with sidebar and main content) const finalKGs = trace.knowledge_graphs?.filter( (kg) => kg.is_final === true || (kg.window_index === null && kg.window_total !== null) ) || []; return total + finalKGs.length; }, 0); const totalEntities = state.traces.reduce((total, trace) => { const entityCount = trace.knowledge_graphs?.reduce((kgTotal, kg) => { return kgTotal + (kg.entity_count || 0); }, 0) || 0; return total + entityCount; }, 0); const totalRelations = state.traces.reduce((total, trace) => { const relationCount = trace.knowledge_graphs?.reduce((kgTotal, kg) => { return kgTotal + (kg.relation_count || 0); }, 0) || 0; return total + relationCount; }, 0); const completedProcessing = state.traces.reduce((total, trace) => { const completedKGs = trace.knowledge_graphs?.filter((kg) => kg.status === "analyzed") || []; return total + completedKGs.length; }, 0); const processingProgress = totalAgentGraphs > 0 ? (completedProcessing / totalAgentGraphs) * 100 : 0; return { totalTraces, totalAgentGraphs, totalEntities, totalRelations, completedProcessing, processingProgress, systemStatus: "healthy", }; }, [state.traces]); // Calculate recent activity using useMemo const recentActivity = useMemo(() => { const activity: ActivityItem[] = []; // Add recent trace uploads state.traces.slice(0, 2).forEach((trace) => { activity.push({ id: `trace-${trace.id}`, action: "Trace Uploaded", trace: trace.filename, timestamp: getRelativeTime( trace.update_timestamp || trace.upload_timestamp || "" ), status: "success", }); }); // Add recent graph generations from traces const allKnowledgeGraphs = state.traces.flatMap( (trace) => trace.knowledge_graphs || [] ); allKnowledgeGraphs .sort((a, b) => { const dateA = new Date(a.created_at || 0); const dateB = new Date(b.created_at || 0); return dateB.getTime() - dateA.getTime(); }) .slice(0, 2) .forEach((kg, index) => { activity.push({ id: `kg-${kg.kg_id || kg.id || index}`, action: "Agent Graph Generated", trace: kg.filename || "Unknown", timestamp: getRelativeTime(kg.created_at), status: kg.status === "analyzed" ? "success" : "processing", }); }); return activity.slice(0, 5); }, [state.traces]); const fetchDashboardData = useCallback(async () => { try { setIsLoading(true); setError(null); // Data is now calculated via useMemo, so we just need to handle loading state setIsLoading(false); } catch (err) { setError( err instanceof Error ? err.message : "Failed to load dashboard data" ); setIsLoading(false); } }, []); useEffect(() => { fetchDashboardData(); }, [fetchDashboardData]); return { stats, recentActivity, isLoading, error, refresh: fetchDashboardData, }; } function getRelativeTime(dateString: string): string { const date = new Date(dateString); const now = new Date(); const diffMs = now.getTime() - date.getTime(); // If the date is invalid, return a default if (isNaN(diffMs)) return "Recently"; const diffMins = Math.floor(diffMs / 60000); const diffHours = Math.floor(diffMs / 3600000); const diffDays = Math.floor(diffMs / 86400000); if (diffMins < 1) return "Just now"; if (diffMins < 60) return `${diffMins} minute${diffMins > 1 ? "s" : ""} ago`; if (diffHours < 24) return `${diffHours} hour${diffHours > 1 ? "s" : ""} ago`; return `${diffDays} day${diffDays > 1 ? "s" : ""} ago`; }