'use client'; import { useEffect, useRef, useState } from 'react'; import api from '@/lib/axios'; interface Stats { nodes: number; edges: number; types: number; documents: number; } function Counter({ value, suffix, decimals = 0 }: { value: number; suffix: string; decimals?: number }) { const [n, setN] = useState(0); const ref = useRef(null); useEffect(() => { const el = ref.current; if (!el) return; const obs = new IntersectionObserver((entries) => { if (entries[0].isIntersecting) { let start = 0; const dur = 1200; const t0 = performance.now(); const tick = (t: number) => { const p = Math.min((t - t0) / dur, 1); setN(value * (1 - Math.pow(1 - p, 3))); if (p < 1) requestAnimationFrame(tick); }; requestAnimationFrame(tick); obs.disconnect(); } }); obs.observe(el); return () => obs.disconnect(); }, [value]); return ( {n.toFixed(decimals)} {suffix} ); } export function StatsCounter() { const [stats, setStats] = useState({ nodes: 0, edges: 0, types: 0, documents: 0 }); useEffect(() => { Promise.all([ api.get('/graph/stats/').catch(() => ({ data: { nodes_count: 0, edges_count: 0, type_distribution: [] } })), api.get('/documents/').catch(() => ({ data: { count: 0 } })), ]).then(([graphRes, docsRes]) => { setStats({ nodes: graphRes.data.nodes_count || 0, edges: graphRes.data.edges_count || 0, types: (graphRes.data.type_distribution || []).length, documents: docsRes.data.count || (docsRes.data.results || []).length || 0, }); }); }, []); const STAT_ITEMS = [ { label: 'Graph Nodes', value: stats.nodes, suffix: '' }, { label: 'Relationships', value: stats.edges, suffix: '' }, { label: 'Entity Types', value: stats.types, suffix: '' }, { label: 'Documents', value: stats.documents, suffix: '' }, ]; if (stats.nodes === 0 && stats.documents === 0) return null; return (

Knowledge Graph Statistics

{STAT_ITEMS.map((s) => (

{s.label}

))}
); }