"use client"; import { createFileRoute, Link, useNavigate } from "@tanstack/react-router"; import { useEffect, useState } from "react"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { toast } from "sonner"; import { ArrowLeft, Loader2, PlayCircle, PauseCircle, Server, Settings, ShieldAlert, } from "lucide-react"; import { supabase } from "@/integrations/supabase/client"; import { useAuth } from "@/hooks/use-auth"; import { invokeFunction } from "@/lib/invoke-function"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Skeleton } from "@/components/ui/skeleton"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; export const Route = createFileRoute("/admin")({ component: AdminPage, }); interface AgentRow { id: string; user_id: string; status: string; uuid_tenant: string; telegram_bot_username: string | null; telegram_bot_token_vault_id: string | null; railway_service_id: string | null; vps_pool_id: string | null; created_at: string; provisioned_at: string | null; profile: { full_name: string | null } | null; subscription: { plans: { slug: string; name: string } | null } | null; } function AdminPage() { const { user, loading: authLoading } = useAuth(); const navigate = useNavigate(); const queryClient = useQueryClient(); const [busy, setBusy] = useState(null); const { data: isAdmin, isLoading: roleLoading } = useQuery({ queryKey: ["is-admin", user?.id], enabled: !!user, queryFn: async () => { const { data, error } = await supabase.rpc("has_role", { _user_id: user!.id, _role: "admin", }); if (error) throw error; return data === true; }, }); const { data: agents, isLoading: agentsLoading } = useQuery({ queryKey: ["agents-admin"], enabled: !!isAdmin, refetchInterval: 15000, queryFn: async () => { const { data: agentsData, error: agentsError } = await supabase .from("agent_instances") .select(`*, profile:profiles!agent_instances_user_id_fkey(full_name, phone)`) .order("created_at", { ascending: false }) .limit(100); if (agentsError) throw agentsError; const userIds = [...new Set((agentsData ?? []).map((agent) => agent.user_id).filter(Boolean))]; const { data: subscriptionsData, error: subscriptionsError } = userIds.length ? await supabase .from("subscriptions") .select("user_id, status, plans(name, slug)") .in("user_id", userIds) .order("created_at", { ascending: false }) : { data: [], error: null }; if (subscriptionsError) throw subscriptionsError; const subscriptionsByUserId = new Map( (subscriptionsData ?? []).map((subscription) => [subscription.user_id, subscription]), ); // deno-lint-ignore no-explicit-any return (agentsData as any[]).map((agent) => ({ ...agent, profile: Array.isArray(agent.profile) ? agent.profile[0] ?? null : agent.profile, subscription: subscriptionsByUserId.get(agent.user_id) ?? null, })) as AgentRow[]; }, }); useEffect(() => { if (!authLoading && !user) { navigate({ to: "/login", search: { redirect: "/admin" } }); } }, [authLoading, user, navigate]); if (authLoading || roleLoading) { return (
); } if (!isAdmin) { return (

Acesso negado

Esta área é restrita a administradores do Mika.

); } async function action(fn: "suspend-agent" | "resume-agent", agentId: string) { setBusy(agentId + fn); const { data, error } = await invokeFunction<{ ok?: boolean; error?: string }>(fn, { agent_instance_id: agentId, }); setBusy(null); if (error) { toast.error(`${fn} falhou: ${error.message}`); } else if (data?.error) { toast.error(`${fn}: ${data.error}`); } else { toast.success(`${fn} executado com sucesso`); queryClient.invalidateQueries({ queryKey: ["agents-admin"] }); } } return (

Admin · Mika

Configure e gerencie agentes provisionados.

Agentes ({agents?.length ?? 0})

{agentsLoading ? ( ) : !agents?.length ? (

Nenhum agente cadastrado ainda.

) : (
Tenant Cliente Bot Plano Status Railway Ações {agents.map((a) => ( {a.uuid_tenant.slice(0, 8)} {a.profile?.full_name || "—"} {a.telegram_bot_username ? `@${a.telegram_bot_username}` : "—"} {a.railway_service_id?.slice(0, 8) ?? "—"} {a.status === "active" && ( )} {a.status === "suspended" && ( )} ))}
)}
); } function StatusBadge({ status }: { status: string }) { if (status === "active") return Ativo; if (status === "provisioning") return Provisionando; if (status === "suspended") return Suspenso; if (status === "error") return Erro; return {status}; } function PlanBadge({ slug }: { slug: string | null }) { if (!slug) return Sem plano; if (slug === "professional" || slug === "enterprise") return {slug}; if (slug === "starter") return {slug}; return {slug}; }