'use client' /** * Agent Card Component * Displays a single agent with status, actions, and metadata. */ import { useState, useEffect } from 'react' import { formatDistanceToNow } from 'date-fns' import { fr } from 'date-fns/locale/fr' import { enUS } from 'date-fns/locale/en-US' import { Play, Trash2, Loader2, ToggleLeft, ToggleRight, Globe, Search, Eye, Settings, CheckCircle2, XCircle, Clock, } from 'lucide-react' import { toast } from 'sonner' import { useLanguage } from '@/lib/i18n' import { getNotebookIcon } from '@/lib/notebook-icon' // --- Types --- interface AgentCardProps { agent: { id: string name: string description?: string | null type?: string | null isEnabled: boolean frequency: string lastRun: string | Date | null createdAt: string | Date updatedAt: string | Date _count: { actions: number } actions: { id: string; status: string; createdAt: string | Date }[] notebook?: { id: string; name: string; icon?: string | null } | null } onEdit: (id: string) => void onRefresh: () => void onToggle: (id: string, isEnabled: boolean) => void } // --- Config --- const typeConfig: Record = { scraper: { icon: Globe, color: 'text-blue-600', bgColor: 'bg-blue-50 border-blue-200' }, researcher: { icon: Search, color: 'text-purple-600', bgColor: 'bg-purple-50 border-purple-200' }, monitor: { icon: Eye, color: 'text-amber-600', bgColor: 'bg-amber-50 border-amber-200' }, custom: { icon: Settings, color: 'text-green-600', bgColor: 'bg-green-50 border-green-200' }, } const frequencyKeys: Record = { manual: 'agents.frequencies.manual', hourly: 'agents.frequencies.hourly', daily: 'agents.frequencies.daily', weekly: 'agents.frequencies.weekly', monthly: 'agents.frequencies.monthly', } const statusKeys: Record = { success: 'agents.status.success', failure: 'agents.status.failure', running: 'agents.status.running', pending: 'agents.status.pending', } // --- Component --- export function AgentCard({ agent, onEdit, onRefresh, onToggle }: AgentCardProps) { const { t, language } = useLanguage() const [isRunning, setIsRunning] = useState(false) const [isDeleting, setIsDeleting] = useState(false) const [isToggling, setIsToggling] = useState(false) const [mounted, setMounted] = useState(false) // Prevent hydration mismatch for date formatting useEffect(() => { setMounted(true) }, []) const config = typeConfig[agent.type || 'scraper'] || typeConfig.custom const Icon = config.icon const lastAction = agent.actions[0] const dateLocale = language === 'fr' ? fr : enUS const isNew = new Date(agent.createdAt).getTime() === new Date(agent.updatedAt).getTime() const handleRun = async () => { setIsRunning(true) try { const { runAgent } = await import('@/app/actions/agent-actions') const result = await runAgent(agent.id) if (result.success) { toast.success(t('agents.toasts.runSuccess', { name: agent.name })) } else { toast.error(t('agents.toasts.runError', { error: result.error || t('agents.toasts.runFailed') })) } } catch { toast.error(t('agents.toasts.runGenericError')) } finally { setIsRunning(false) onRefresh() } } const handleDelete = async () => { if (!confirm(t('agents.actions.deleteConfirm', { name: agent.name }))) return setIsDeleting(true) try { const { deleteAgent } = await import('@/app/actions/agent-actions') await deleteAgent(agent.id) toast.success(t('agents.toasts.deleted', { name: agent.name })) } catch { toast.error(t('agents.toasts.deleteError')) } finally { setIsDeleting(false) onRefresh() } } const handleToggle = async () => { const newEnabled = !agent.isEnabled setIsToggling(true) onToggle(agent.id, newEnabled) try { const { toggleAgent } = await import('@/app/actions/agent-actions') await toggleAgent(agent.id, newEnabled) toast.success(newEnabled ? t('agents.actions.toggleOn') : t('agents.actions.toggleOff')) } catch { onToggle(agent.id, !newEnabled) toast.error(t('agents.toasts.toggleError')) } finally { setIsToggling(false) } } return (

{agent.name}

{mounted && isNew && ( {t('agents.newBadge')} )}
{t(`agents.types.${agent.type || 'custom'}`)}
{agent.description && (

{agent.description}

)}
{t(frequencyKeys[agent.frequency] || 'agents.frequencies.manual')} {agent.notebook && ( {(() => { const Icon = getNotebookIcon(agent.notebook.icon) return })()} {agent.notebook.name} )} {t('agents.metadata.executions', { count: agent._count.actions })}
{lastAction && (
{lastAction.status === 'success' && } {lastAction.status === 'failure' && } {lastAction.status === 'running' && } {t(statusKeys[lastAction.status] || lastAction.status)} {' - '} {mounted ? formatDistanceToNow(new Date(lastAction.createdAt), { addSuffix: true, locale: dateLocale }) : new Date(lastAction.createdAt).toISOString().split('T')[0]}
)}
) }