/** * Real Data Hook - Fetches live data from WidgetTDC Backend * Replaces mock/simulated data with real API data */ import { useState, useEffect, useCallback } from 'react'; import { API_URL } from '../config/api'; // Types for real data export interface LogEntry { id: string; timestamp: string; level: 'debug' | 'info' | 'warn' | 'error'; source: string; message: string; } export interface GraphStats { nodes: number; relationships: number; timestamp: string; } export interface SystemHealth { status: string; registeredTools: number; services: { neo4j: { healthy: boolean }; vectorStore: { healthy: boolean; backend: string }; }; } export interface HyperMetrics { totalThoughts: number; toolUsageRate: number; activeAgents: number; } export interface RealDataState { logs: LogEntry[]; graphStats: GraphStats | null; health: SystemHealth | null; hyperMetrics: HyperMetrics | null; isLoading: boolean; error: string | null; lastUpdated: number; } export const useRealData = (refreshInterval = 5000) => { const [state, setState] = useState({ logs: [], graphStats: null, health: null, hyperMetrics: null, isLoading: true, error: null, lastUpdated: 0, }); const fetchData = useCallback(async () => { try { const [logsRes, graphRes, healthRes, hyperRes] = await Promise.all([ fetch(`${API_URL}/api/logs`).catch(() => null), fetch(`${API_URL}/api/evolution/graph/stats`).catch(() => null), fetch(`${API_URL}/health`).catch(() => null), fetch(`${API_URL}/api/hyper/status`).catch(() => null), ]); const logs = logsRes?.ok ? (await logsRes.json()).entries?.slice(0, 50) || [] : []; const graphStats = graphRes?.ok ? await graphRes.json() : null; const health = healthRes?.ok ? await healthRes.json() : null; const hyperData = hyperRes?.ok ? await hyperRes.json() : null; setState({ logs, graphStats: graphStats ? { nodes: graphStats.stats?.nodes || 0, relationships: graphStats.stats?.relationships || 0, timestamp: graphStats.timestamp, } : null, health: health ? { status: health.status, registeredTools: health.registeredTools || 0, services: health.services || {}, } : null, hyperMetrics: hyperData?.metrics || null, isLoading: false, error: null, lastUpdated: Date.now(), }); } catch (err) { setState(prev => ({ ...prev, isLoading: false, error: (err as Error).message, })); } }, []); useEffect(() => { fetchData(); const interval = setInterval(fetchData, refreshInterval); return () => clearInterval(interval); }, [fetchData, refreshInterval]); // Derived metrics from real data const metrics = { totalNodes: state.graphStats?.nodes || 0, totalRelationships: state.graphStats?.relationships || 0, totalTools: state.health?.registeredTools || 0, activeAgents: state.hyperMetrics?.activeAgents || 0, systemStatus: state.health?.status || 'unknown', neo4jHealthy: state.health?.services?.neo4j?.healthy || false, vectorStoreHealthy: state.health?.services?.vectorStore?.healthy || false, recentErrors: state.logs.filter(l => l.level === 'error').length, recentWarnings: state.logs.filter(l => l.level === 'warn').length, }; // Transform logs to events for widgets const events = state.logs.map(log => ({ id: log.id, time: new Date(log.timestamp).toLocaleTimeString('da-DK'), message: log.message, level: log.level, source: log.source, })); return { ...state, metrics, events, refresh: fetchData, }; }; // Hook for real-time log streaming export const useLogStream = (maxLogs = 20) => { const [logs, setLogs] = useState([]); useEffect(() => { const fetchLogs = async () => { try { const res = await fetch(`${API_URL}/api/logs`); if (res.ok) { const data = await res.json(); setLogs(data.entries?.slice(0, maxLogs) || []); } } catch { // Silent fail } }; fetchLogs(); const interval = setInterval(fetchLogs, 3000); return () => clearInterval(interval); }, [maxLogs]); return logs; }; export default useRealData;