"use client"; import { createFileRoute, Link, useNavigate } from "@tanstack/react-router"; import { ArrowLeft, Clock, AlertTriangle, Pause, Play, Trash2 } from "lucide-react"; import { toast } from "sonner"; import { useState } from "react"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog"; import { type ScheduledJob, useCronjob, useDeleteCronjob, useUpdateCronjobStatus, } from "@/hooks/use-cronjobs"; import { useAvailableMcps, useUserIntegrations } from "@/hooks/use-integrations"; import { syncAgentRuntime } from "@/lib/sync-agent-runtime"; export const Route = createFileRoute("/painel/cronjobs/$id")({ component: CronjobDetailPage, }); function CronjobDetailPage() { const { id } = Route.useParams(); const navigate = useNavigate(); const { data: job, isLoading, error } = useCronjob(id); const { data: mcps = [] } = useAvailableMcps(); const { data: integrations = [] } = useUserIntegrations(); const updateMut = useUpdateCronjobStatus(); const deleteMut = useDeleteCronjob(); const [confirmDelete, setConfirmDelete] = useState(false); if (isLoading) { return
; } if (error || !job) { return (
Automação não encontrada.
); } const connectedMcpIds = new Set( integrations.filter((i) => i.status === "active").map((i) => i.mcp_id), ); const requiredMcps = job.required_mcp_slugs .map((slug) => mcps.find((m) => m.slug === slug)) .filter((m): m is NonNullable => !!m); const missingMcps = requiredMcps.filter((m) => !connectedMcpIds.has(m.id)); const isActive = job.status === "active"; const isAutoPaused = job.status === "auto_paused"; const hasRuntimeError = job.runtime_state === "error" || job.runtime_last_status === "error"; async function toggle() { try { await updateMut.mutateAsync({ id: job!.id, status: isActive ? "paused" : "active", }); toast.success(isActive ? "Pausada." : "Ativada."); const { error: syncError } = await syncAgentRuntime(job!.agent_instance_id, "cronjobs"); if (syncError) { toast.warning("Status salvo, mas o runtime do agente não sincronizou."); } } catch (e) { toast.error(e instanceof Error ? e.message : "Erro"); } } async function remove() { try { await deleteMut.mutateAsync(job!.id); toast.success("Excluída."); const { error: syncError } = await syncAgentRuntime(job!.agent_instance_id, "cronjobs"); if (syncError) { toast.warning("Automação removida, mas o runtime do agente não sincronizou."); } navigate({ to: "/painel/cronjobs" }); } catch (e) { toast.error(e instanceof Error ? e.message : "Erro"); } } return (

{job.name}

{isActive && Ativa} {job.status === "paused" && Pausada} {job.status === "error" && Erro} {isAutoPaused && ( Auto-pausada )} {hasRuntimeError && !isAutoPaused && ( Runtime com erro )}
{isAutoPaused && job.auto_paused_reason && (

Auto-pausada

{job.auto_paused_reason}

)} {hasRuntimeError && (

Falha reportada pelo runtime

{job.runtime_last_error ?? "O runtime marcou a última execução como erro."}

)} {job.runtime_last_delivery_error && (

Falha na entrega do resultado

{job.runtime_last_delivery_error}

)} {missingMcps.length > 0 && isActive && (

Integrações necessárias não conectadas:

    {missingMcps.map((m) => (
  • {m.name}
  • ))}
)}
} />
{job.description &&
}

Ação enviada ao agente

            {job.action_prompt}
          

Integrações necessárias

{requiredMcps.length === 0 ? (

Nenhuma.

) : (
{requiredMcps.map((m) => { const ok = connectedMcpIds.has(m.id); return ( {m.name} {ok ? "✓" : "(não conectado)"} ); })}
)}
Excluir automação? Esta ação remove permanentemente “{job.name}”. Cancelar { e.preventDefault(); remove(); }} disabled={deleteMut.isPending} className="bg-destructive text-destructive-foreground hover:bg-destructive/90" > {deleteMut.isPending ? "Excluindo..." : "Excluir"}
); } function Section({ label, value, mono, icon, }: { label: string; value: string; mono?: boolean; icon?: React.ReactNode; }) { return (

{label}

{icon} {value}

); } function formatRuntimeState(state: ScheduledJob["runtime_state"]): string { switch (state) { case "scheduled": return "Agendado"; case "paused": return "Pausado"; case "completed": return "Concluído"; case "error": return "Erro"; default: return "Desconhecido"; } } function formatRuntimeLastStatus(status: ScheduledJob["runtime_last_status"]): string { switch (status) { case "ok": return "Sucesso"; case "error": return "Erro"; default: return "Ainda sem execução"; } }