import { useState, useEffect, useRef } from 'react'; import { t } from '../i18n'; import type { AgentExecution, AgentExecutionStep, ExecutionMode, ExecutionStatus, SpecialistType } from '../types'; // ── Helpers ────────────────────────────────────────────────────────────────── const TERMINAL_STATUSES: AgentExecution['status'][] = ['done', 'error', 'cancelled']; const STATUS_COLORS: Record = { idle: 'bg-gray-700 text-gray-300', planning: 'bg-yellow-900/50 text-yellow-300', dispatching: 'bg-orange-900/50 text-orange-300', running: 'bg-blue-900/50 text-blue-300', aggregating: 'bg-purple-900/50 text-purple-300', reflecting: 'bg-violet-900/50 text-violet-300', done: 'bg-green-900/50 text-green-300', error: 'bg-red-900/50 text-red-400', cancelled: 'bg-gray-700 text-gray-400', }; const STEP_STATUS_COLORS: Record = { pending: 'bg-gray-700 text-gray-400', running: 'bg-blue-900/50 text-blue-300', done: 'bg-green-900/50 text-green-300', error: 'bg-red-900/50 text-red-400', }; const SPECIALIST_ICONS: Record = { researcher: '🔬', writer: '✍️', analyst: '📊', executor: '⚙️', critic: '📝', }; function formatDuration(ms: number | null): string { if (ms == null) return ''; if (ms < 1000) return `${ms}ms`; if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`; return `${(ms / 60000).toFixed(1)}m`; } function formatDatetime(ts: string): string { try { return new Date(ts).toLocaleString([], { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', }); } catch { return ts; } } function truncate(str: string, max = 120): string { if (!str) return ''; return str.length > max ? str.slice(0, max) + '…' : str; } // ── Sub-components ──────────────────────────────────────────────────────────── interface StatusBadgeProps { status: AgentExecution['status']; } function StatusBadge({ status }: StatusBadgeProps) { return ( {status} ); } interface StepStatusBadgeProps { status: AgentExecutionStep['status']; } function StepStatusBadge({ status }: StepStatusBadgeProps) { return ( {status} ); } // ── Create Form ─────────────────────────────────────────────────────────────── interface CreateFormProps { onCreate: (execution: AgentExecution) => void; } function CreateForm({ onCreate }: CreateFormProps) { const [goal, setGoal] = useState(''); const [mode, setMode] = useState('auto'); const [saving, setSaving] = useState(false); const [error, setError] = useState(''); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!goal.trim()) { setError(t('executions.goalRequired') || 'Goal is required'); return; } setSaving(true); setError(''); try { const res = await fetch('/api/executions', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ goal: goal.trim(), mode }), }); if (!res.ok) throw new Error(`HTTP ${res.status}`); const created: AgentExecution = await res.json(); onCreate(created); setGoal(''); } catch (err) { setError(`${t('executions.createFailed') || 'Create failed'}: ${(err as Error).message}`); } finally { setSaving(false); } }; return (

{t('executions.newExecution') || 'New Execution'}