import { useEffect, useState, useCallback } from 'react'; import type { ProjectSummary, AgentStatus, AuditLog } from '../api/client'; import { api } from '../api/client'; import { useWebSocket } from '../api/useWebSocket'; const STATUS_COLORS: Record = { COMPLETED: 'badge-completed', FAILED: 'badge-failed', PAUSED: 'badge-paused', }; function getStatusBadge(status: string) { if (status.endsWith('_RUNNING')) return 'badge-running'; if (status.startsWith('AWAITING_')) return 'badge-awaiting'; return STATUS_COLORS[status] || 'badge-awaiting'; } const STATUS_ICONS: Record = { COMPLETED: '✅', FAILED: '❌', PAUSED: '⏸️', }; function getStatusIcon(s: string) { if (s.endsWith('_RUNNING')) return '⚡'; if (s.startsWith('AWAITING_')) return '⏳'; return STATUS_ICONS[s] || '❓'; } export default function DashboardPage() { const [projects, setProjects] = useState([]); const [agents, setAgents] = useState([]); const [logs, setLogs] = useState([]); const [loading, setLoading] = useState(true); const fetchAll = useCallback(async () => { try { const [p, a, l] = await Promise.all([ api.listProjects(), api.listAgents(), api.listLogs({ limit: '10' }), ]); setProjects(p); setAgents(a); setLogs(l); } catch { /* ignore */ } setLoading(false); }, []); useEffect(() => { fetchAll(); }, [fetchAll]); const { connected } = useWebSocket(useCallback((msg) => { if (msg.type === 'project_update' || msg.type === 'agent_status') fetchAll(); }, [fetchAll])); const active = projects.filter(p => !['COMPLETED', 'FAILED'].includes(p.status)); const completed = projects.filter(p => p.status === 'COMPLETED'); const runningAgents = agents.filter(a => a.current_status === 'running'); if (loading) { return (
🦊
); } return (
{/* Connection indicator */}
{connected ? 'Connecté en temps réel' : 'Déconnecté — reconnexion...'}
{/* Stats Cards */}
{/* Active Projects */}

Projets en cours

{active.length === 0 ? (

Aucun projet actif

) : (
{active.map((p) => (
{p.name}
{getStatusIcon(p.status)} {p.status.replace(/_/g, ' ')} {p.workflow_type.replace(/_/g, ' ')}
{p.tasks_done}/{p.task_count} tâches
0 ? (p.tasks_done / p.task_count) * 100 : 0}%` }} >
))}
)}
{/* Agents + Recent Logs */}
{/* Agent Grid */}

🤖 Agents

{agents.map((a) => (
{a.current_status === 'running' ? '⚡' : a.current_status === 'failed' ? '❌' : '💤'}
{a.display_name}
{a.model}
{a.current_status}
))}
{/* Recent Logs */}

📜 Activité récente

{logs.length === 0 ? (

Aucune activité

) : logs.map((l) => (
{new Date(l.timestamp).toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' })} {l.agent} {l.message || l.action}
))}
); } function StatCard({ icon, label, value, accent }: { icon: string; label: string; value: number; accent: string }) { const bgMap: Record = { fox: 'from-fox-600/10 to-fox-700/5', green: 'from-green-500/10 to-green-600/5', blue: 'from-blue-500/10 to-blue-600/5', purple: 'from-purple-500/10 to-purple-600/5', }; const textMap: Record = { fox: 'text-fox-500', green: 'text-green-400', blue: 'text-blue-400', purple: 'text-purple-400', }; return (
{label}
{value}
{icon}
); }