import { useState, useEffect } from 'react' import { motion } from 'framer-motion' import { AlertTriangle, CheckCircle2, Clock, TrendingUp, Zap, RefreshCw, AlertCircle, } from 'lucide-react' interface Evaluation { id: number source: string entity_id: string result_real: string result_nominal: string confidence: number evaluated_at: number category: string judge_method: string } interface DivergenceStats { total_divergences: number by_category: Record by_judge: Record recent_24h: number high_confidence: number } interface DivergenceData { divergences: Evaluation[] stats: DivergenceStats generated_at: string } const containerVariants = { hidden: { opacity: 0 }, visible: { opacity: 1, transition: { staggerChildren: 0.1 }, }, } const itemVariants = { hidden: { opacity: 0, y: 20 }, visible: { opacity: 1, y: 0, transition: { duration: 0.4 }, }, } const StatusBadge = ({ status }: { status: string }) => { const styles: Record = { 'automation-liveness': { bg: 'bg-red-500/10', text: 'text-red-400', icon: , }, 'automation-failure': { bg: 'bg-orange-500/10', text: 'text-orange-400', icon: , }, } const style = styles[status] || styles['automation-failure'] return (
{style.icon} {status.replace('-', ' ')}
) } const StatCard = ({ label, value, icon: Icon, color = 'text-blue-400', }: { label: string value: number | string icon: React.ElementType color?: string }) => (

{label}

{value}

) const DivergenceRow = ({ div }: { div: Evaluation }) => { const timestamp = new Date(div.evaluated_at * 1000).toLocaleString('pt-PT') const confidenceColor = div.confidence >= 0.95 ? 'bg-green-500' : div.confidence >= 0.9 ? 'bg-yellow-500' : 'bg-orange-500' return ( {div.id} {div.entity_id} {div.result_real} {div.result_nominal}
{(div.confidence * 100).toFixed(0)}%
{timestamp} ) } export default function StackObserver() { const [data, setData] = useState(null) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const [lastUpdate, setLastUpdate] = useState(null) const fetchData = async () => { try { setLoading(true) setError(null) const response = await fetch('/api/stack-observer') if (!response.ok) throw new Error(`API error: ${response.status}`) const result = await response.json() as DivergenceData setData(result) setLastUpdate(new Date()) } catch (err: unknown) { const message = err instanceof Error ? err.message : 'Failed to fetch divergences' setError(message) console.error('Stack Observer error:', err) } finally { setLoading(false) } } useEffect(() => { fetchData() // Refresh every 30 seconds const interval = setInterval(fetchData, 30000) return () => clearInterval(interval) }, []) const refreshTime = lastUpdate ? lastUpdate.toLocaleTimeString('pt-PT', { hour: '2-digit', minute: '2-digit', second: '2-digit' }) : 'nunca' return ( {/* Header */}

Stack Observer

Monitorização de divergências de automações

Última actualização: {refreshTime} • Auto-refresh a cada 30s

{error && ( {error} )} {loading && !data ? (

Carregando divergências...

) : data ? ( <> {/* Stats Grid */} {/* Divergences Table */}

Divergências Detectadas

{data.divergences.length === 0 ? (

Nenhuma divergência detectada

) : (
{data.divergences.slice(0, 20).map((div) => ( ))}
ID Entidade Categoria Resultado Real Resultado Nominal Confiança Timestamp
)} {data.divergences.length > 20 && (
Mostrando 20 de {data.divergences.length} divergências
)}
) : null}
) }