"use client"; import { useState } from "react"; import { createFileRoute, Link, useNavigate } from "@tanstack/react-router"; import { useQueryClient } from "@tanstack/react-query"; import { toast } from "sonner"; import { ArrowLeft, RefreshCw, CheckCircle2, AlertCircle, Clock, Unplug, Activity, } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { useAvailableMcps, useUserIntegrations, useDependentJobsForMcp, } from "@/hooks/use-integrations"; import { invokeFunction } from "@/lib/invoke-function"; import { DisconnectMCPDialog } from "@/components/mika/integrations/DisconnectMCPDialog"; export const Route = createFileRoute("/painel/integracoes/$slug")({ component: IntegrationDetailPage, }); function formatPtBR(date: string | null): string { if (!date) return "—"; try { return new Intl.DateTimeFormat("pt-BR", { day: "2-digit", month: "2-digit", year: "numeric", hour: "2-digit", minute: "2-digit", }).format(new Date(date)); } catch { return "—"; } } function IntegrationDetailPage() { const { slug } = Route.useParams(); const navigate = useNavigate(); const queryClient = useQueryClient(); const { data: mcps = [], isLoading: mcpsLoading } = useAvailableMcps(); const { data: integs = [], isLoading: integsLoading } = useUserIntegrations(); const { data: dependentJobs = [] } = useDependentJobsForMcp(slug); const [testing, setTesting] = useState(false); const [refreshing, setRefreshing] = useState(false); const [disconnectOpen, setDisconnectOpen] = useState(false); const mcp = mcps.find((m) => m.slug === slug); const integration = mcp ? integs.find((i) => i.mcp_id === mcp.id) : null; if (mcpsLoading || integsLoading) { return (
); } if (!mcp) { return (

Integração não encontrada.

); } if (!integration) { return (

Você ainda não conectou {mcp.name}.

); } async function handleTest() { setTesting(true); const { data, error } = await invokeFunction<{ ok?: boolean; success?: boolean; account?: string; account_info?: unknown; }>( "test-integration", { integration_id: integration!.id }, ); setTesting(false); if (error) { toast.error(error.message); } else if (data?.ok || data?.success) { toast.success("Conexão funcionando perfeitamente."); } else { toast.warning("Conexão respondeu, mas com aviso. Verifique status."); } queryClient.invalidateQueries({ queryKey: ["user-integrations"] }); } async function handleRefresh() { setRefreshing(true); const { data, error } = await invokeFunction<{ success: boolean; expires_at: string | null; runtime_sync_warning?: string | null; }>("refresh-integration-token", { integration_id: integration!.id, }); setRefreshing(false); if (error) { toast.error(error.message); } else { toast.success("Token renovado."); if (data?.runtime_sync_warning) { toast.warning("Token renovado, mas o runtime do agente não sincronizou."); } } queryClient.invalidateQueries({ queryKey: ["user-integrations"] }); } const status = integration.status; const statusBadge = status === "active" ? ( Ativo ) : status === "expired" ? ( Expirado ) : status === "revoked" ? ( Revogado ) : ( Erro ); return (

{mcp.name}

{statusBadge}

{mcp.description}

{integration.error_message && (
{integration.error_message}
)}

Conta conectada

Email
{integration.connected_account_email ?? "—"}
{integration.connected_account_name && (
Nome
{integration.connected_account_name}
)}
Conectado em
{formatPtBR(integration.created_at)}
Última renovação
{formatPtBR(integration.last_refreshed_at)}
{integration.token_expires_at && (
Token expira em
{formatPtBR(integration.token_expires_at)}
)}

Permissões concedidas

{integration.granted_scopes.length === 0 ? (

Nenhum escopo registrado.

) : (
    {integration.granted_scopes.map((s) => (
  • {s}
  • ))}
)}
{dependentJobs.length > 0 && (

Automações que usam esta integração

    {dependentJobs.map((j) => (
  • {j.name} {j.status}
  • ))}
)}
{mcp.supports_refresh_token && ( )} {(status === "expired" || status === "revoked" || status === "error") && ( )}
navigate({ to: "/painel/integracoes", search: {} })} integrationId={integration.id} mcpSlug={mcp.slug} mcpName={mcp.name} />
); }