Some checks failed
Deploy to Production / Build and Deploy (push) Failing after 1m7s
Replaced ~100+ hardcoded French and English text strings across 30+ components with proper i18n t() calls. Added 57 new translation keys to all 15 locale files (ar, de, en, es, fa, fr, hi, it, ja, ko, nl, pl, pt, ru, zh). Key changes: - contextual-ai-chat.tsx: 30 French strings → t() (actions, toasts, labels, placeholders) - ai-chat.tsx: 15 French/English strings → t() (header, tabs, welcome, insights, history) - note-inline-editor.tsx: 20 French fallbacks removed (toolbar, save status, checklist) - lab-skeleton.tsx: French loading text → t() - admin-header.tsx, header.tsx, editor-connections-section.tsx: French fallbacks removed - New AI chat component, agent cards, sidebar, settings panel i18n cleanup Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
174 lines
6.7 KiB
TypeScript
174 lines
6.7 KiB
TypeScript
'use client'
|
|
|
|
/**
|
|
* Agent Run Log
|
|
* Shows execution history for an agent.
|
|
*/
|
|
|
|
import { useState, useEffect } from 'react'
|
|
import { X, CheckCircle2, XCircle, Loader2, Clock, ChevronDown, Wrench } from 'lucide-react'
|
|
import { formatDistanceToNow } from 'date-fns'
|
|
import { fr } from 'date-fns/locale/fr'
|
|
import { enUS } from 'date-fns/locale/en-US'
|
|
import { useLanguage } from '@/lib/i18n'
|
|
|
|
interface AgentRunLogProps {
|
|
agentId: string
|
|
agentName: string
|
|
onClose: () => void
|
|
}
|
|
|
|
interface Action {
|
|
id: string
|
|
status: string
|
|
result?: string | null
|
|
log?: string | null
|
|
input?: string | null
|
|
toolLog?: string | null
|
|
tokensUsed?: number | null
|
|
createdAt: string | Date
|
|
}
|
|
|
|
interface ToolLogStep {
|
|
step: number
|
|
text?: string
|
|
toolCalls?: Array<{ toolName: string; args: any }>
|
|
toolResults?: Array<{ toolName: string; preview?: string }>
|
|
}
|
|
|
|
const statusKeys: Record<string, string> = {
|
|
success: 'agents.status.success',
|
|
failure: 'agents.status.failure',
|
|
running: 'agents.status.running',
|
|
pending: 'agents.status.pending',
|
|
}
|
|
|
|
export function AgentRunLog({ agentId, agentName, onClose }: AgentRunLogProps) {
|
|
const { t, language } = useLanguage()
|
|
const [actions, setActions] = useState<Action[]>([])
|
|
const [loading, setLoading] = useState(true)
|
|
const dateLocale = language === 'fr' ? fr : enUS
|
|
|
|
useEffect(() => {
|
|
async function load() {
|
|
try {
|
|
const { getAgentActions } = await import('@/app/actions/agent-actions')
|
|
const data = await getAgentActions(agentId)
|
|
setActions(data)
|
|
} catch {
|
|
// Silent fail
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
load()
|
|
}, [agentId])
|
|
|
|
return (
|
|
<div className="fixed inset-0 bg-black/20 flex justify-end z-50" onClick={onClose}>
|
|
<div
|
|
className="bg-card shadow-2xl w-full max-w-md h-full overflow-y-auto animate-in slide-in-from-right duration-300 flex flex-col border-l border-border/40"
|
|
onClick={e => e.stopPropagation()}
|
|
>
|
|
{/* Header */}
|
|
<div className="flex items-center justify-between px-5 py-4 border-b border-border">
|
|
<div>
|
|
<h3 className="font-semibold text-card-foreground">{t('agents.runLog.title')}</h3>
|
|
<p className="text-xs text-muted-foreground">{agentName}</p>
|
|
</div>
|
|
<button onClick={onClose} className="p-1 rounded-md hover:bg-accent">
|
|
<X className="w-5 h-5 text-muted-foreground" />
|
|
</button>
|
|
</div>
|
|
|
|
{/* List */}
|
|
<div className="flex-1 overflow-y-auto p-4 space-y-2">
|
|
{loading && (
|
|
<div className="flex items-center justify-center py-8">
|
|
<Loader2 className="w-5 h-5 animate-spin text-muted-foreground" />
|
|
</div>
|
|
)}
|
|
|
|
{!loading && actions.length === 0 && (
|
|
<p className="text-center text-sm text-muted-foreground py-8">
|
|
{t('agents.runLog.noHistory')}
|
|
</p>
|
|
)}
|
|
|
|
{actions.map(action => {
|
|
let toolSteps: ToolLogStep[] = []
|
|
try {
|
|
toolSteps = action.toolLog ? JSON.parse(action.toolLog) : []
|
|
} catch {}
|
|
|
|
return (
|
|
<div
|
|
key={action.id}
|
|
className={`
|
|
p-3 rounded-lg border
|
|
${action.status === 'success' ? 'bg-green-50/50 dark:bg-green-950/50 border-green-100 dark:border-green-900' : ''}
|
|
${action.status === 'failure' ? 'bg-red-50/50 dark:bg-red-950/50 border-red-100 dark:border-red-900' : ''}
|
|
${action.status === 'running' ? 'bg-blue-50/50 dark:bg-blue-950/50 border-blue-100 dark:border-blue-900' : ''}
|
|
${action.status === 'pending' ? 'bg-muted border-border' : ''}
|
|
`}
|
|
>
|
|
<div className="flex items-start gap-3">
|
|
<div className="mt-0.5">
|
|
{action.status === 'success' && <CheckCircle2 className="w-4 h-4 text-green-500" />}
|
|
{action.status === 'failure' && <XCircle className="w-4 h-4 text-red-500" />}
|
|
{action.status === 'running' && <Loader2 className="w-4 h-4 text-blue-500 animate-spin" />}
|
|
{action.status === 'pending' && <Clock className="w-4 h-4 text-muted-foreground" />}
|
|
</div>
|
|
<div className="flex-1 min-w-0">
|
|
<div className="flex items-center justify-between">
|
|
<span className="text-sm font-medium text-foreground">
|
|
{t(statusKeys[action.status] || action.status)}
|
|
</span>
|
|
<span className="text-xs text-muted-foreground">
|
|
{formatDistanceToNow(new Date(action.createdAt), { addSuffix: true, locale: dateLocale })}
|
|
</span>
|
|
</div>
|
|
{action.log && (
|
|
<p className="text-xs text-muted-foreground mt-1 line-clamp-2">{action.log}</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Tool trace */}
|
|
{toolSteps.length > 0 && (
|
|
<details className="mt-2">
|
|
<summary className="flex items-center gap-1.5 text-xs text-primary cursor-pointer hover:text-primary/80 font-medium">
|
|
<Wrench className="w-3 h-3" />
|
|
{t('agents.runLog.toolTrace', { count: toolSteps.length })}
|
|
<ChevronDown className="w-3 h-3" />
|
|
</summary>
|
|
<div className="mt-2 space-y-2 pl-2">
|
|
{toolSteps.map((step, i) => (
|
|
<div key={i} className="text-xs border-l-2 border-primary/30 pl-2 py-1">
|
|
<span className="font-medium text-muted-foreground">{t('agents.runLog.step', { num: step.step })}</span>
|
|
{step.toolCalls && step.toolCalls.length > 0 && (
|
|
<div className="mt-1 space-y-1">
|
|
{step.toolCalls.map((tc, j) => (
|
|
<div key={j} className="bg-muted rounded px-2 py-1">
|
|
<span className="font-mono text-primary">{tc.toolName}</span>
|
|
<span className="text-muted-foreground ml-1">
|
|
{JSON.stringify(tc.args).substring(0, 80)}
|
|
</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</details>
|
|
)}
|
|
</div>
|
|
)
|
|
})}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|