"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}
/>
);
}