"use client"; import { createFileRoute, useNavigate } from "@tanstack/react-router"; import { useEffect, useMemo, useRef, useState } from "react"; import { motion, AnimatePresence } from "framer-motion"; import { ArrowRight, CheckCircle2, ExternalLink, Loader2, RefreshCcw, Sparkles, } from "lucide-react"; import { useQueryClient } from "@tanstack/react-query"; import { toast } from "sonner"; import { useAuth } from "@/hooks/use-auth"; import { useProfile } from "@/hooks/use-profile"; import { useAgentInstance } from "@/hooks/use-agent-instance"; import { supabase } from "@/integrations/supabase/client"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Logo } from "@/components/mika/Logo"; import { TelegramIcon } from "@/components/mika/telegram/TelegramIcon"; import { TelegramOnboardingWizard } from "@/components/mika/telegram/TelegramOnboardingWizard"; import { cn } from "@/lib/utils"; const WELCOME_DONE_KEY = "mika-welcome-done"; export const Route = createFileRoute("/bem-vindo")({ component: WelcomePage, }); function WelcomePage() { const { user, loading: authLoading } = useAuth(); const { data: profile } = useProfile(); const { data: agent } = useAgentInstance(); const navigate = useNavigate(); const queryClient = useQueryClient(); const [step, setStep] = useState<1 | 2 | 3>(1); const [agentName, setAgentName] = useState(""); const [saving, setSaving] = useState(false); const [wizardOpen, setWizardOpen] = useState(false); // Managed bot state const [creatingBot, setCreatingBot] = useState(false); const [waitingConfirm, setWaitingConfirm] = useState(false); const [waitTimedOut, setWaitTimedOut] = useState(false); const [previewUsername, setPreviewUsername] = useState(null); const waitStartedAt = useRef(null); const fullName = (profile?.full_name || "").trim(); const firstName = useMemo( () => (fullName.split(" ")[0] || "você").trim(), [fullName], ); // Auth guard useEffect(() => { if (!authLoading && !user) { navigate({ to: "/login", search: { redirect: "/bem-vindo" } }); } }, [authLoading, user, navigate]); // Pré-preenche o input com o default useEffect(() => { if (agentName) return; if (agent?.agent_name) { setAgentName(agent.agent_name); } else if (firstName && firstName !== "você") { setAgentName(`Mika de ${firstName}`); } }, [agent?.agent_name, firstName, agentName]); // Etapa 1 → 2 automático em 3s useEffect(() => { if (step !== 1) return; const t = setTimeout(() => setStep(2), 3000); return () => clearTimeout(t); }, [step]); // Se já completou o onboarding, vai direto para /painel useEffect(() => { if (!agent) return; if (agent.onboarding_completed) { navigate({ to: "/painel", search: {} }); } }, [agent, navigate]); async function handleSaveName() { const trimmed = agentName.trim(); if (trimmed.length < 2) { toast.error("O nome precisa ter pelo menos 2 caracteres."); return; } if (!agent) { toast.error("Aguarde, ainda estamos preparando seu agente…"); return; } setSaving(true); const { error } = await supabase .from("agent_instances") .update({ agent_name: trimmed }) .eq("id", agent.id); setSaving(false); if (error) { toast.error("Não foi possível salvar o nome. Tente novamente."); return; } if (user) { await queryClient.invalidateQueries({ queryKey: ["agent-instance", user.id] }); } setStep(3); } async function markWelcomeDone() { if (typeof window !== "undefined") { window.localStorage.setItem(WELCOME_DONE_KEY, "1"); } if (agent) { await supabase .from("agent_instances") .update({ onboarding_completed: true }) .eq("id", agent.id); if (user) { await queryClient.invalidateQueries({ queryKey: ["agent-instance", user.id] }); } } } async function handleOpenManualWizard() { await markWelcomeDone(); setWaitingConfirm(false); setWizardOpen(true); } async function handleSkip() { await markWelcomeDone(); navigate({ to: "/painel", search: {} }); } // Sugere username localmente (apenas preview visual antes do clique) useEffect(() => { if (step !== 3) return; if (agent?.managed_bot_suggested_username) { setPreviewUsername(agent.managed_bot_suggested_username); return; } const base = (agentName || "mika") .toLowerCase() .normalize("NFD") .replace(/[\u0300-\u036f]/g, "") .replace(/[^a-z0-9]/g, "") .substring(0, 28); const safe = base.length >= 3 ? base : `mika${base}`; setPreviewUsername(`${safe}bot`); }, [step, agentName, agent?.managed_bot_suggested_username]); async function handleCreateManagedBot() { if (!agent) { toast.error("Aguarde, ainda estamos preparando seu agente…"); return; } setCreatingBot(true); setWaitTimedOut(false); try { const { data, error } = await supabase.functions.invoke<{ url: string; suggested_username: string; manager_username: string; }>("create-managed-bot", { body: { agent_instance_id: agent.id, agent_name: agentName.trim(), }, }); if (error || !data?.url) { throw error ?? new Error("Resposta inválida"); } setPreviewUsername(data.suggested_username); window.open(data.url, "_blank", "noopener,noreferrer"); await markWelcomeDone(); waitStartedAt.current = Date.now(); setWaitingConfirm(true); } catch (err) { console.error(err); toast.error( "Não foi possível iniciar a criação do bot. Tente novamente ou use o modo manual.", ); } finally { setCreatingBot(false); } } // Polling: enquanto aguardamos confirmação, useAgentInstance já refetch a cada 10s. // Aceleramos o refetch a cada 3s e detectamos sucesso. useEffect(() => { if (!waitingConfirm) return; if (!user) return; const interval = setInterval(() => { queryClient.invalidateQueries({ queryKey: ["agent-instance", user.id] }); if (waitStartedAt.current && Date.now() - waitStartedAt.current > 5 * 60_000) { setWaitingConfirm(false); setWaitTimedOut(true); } }, 3000); return () => clearInterval(interval); }, [waitingConfirm, user, queryClient]); // Detecta sucesso da confirmação via webhook useEffect(() => { if (!waitingConfirm) return; if (agent?.telegram_onboarding_completed && !agent.managed_bot_pending) { setWaitingConfirm(false); toast.success("🎉 Bot criado! Seu agente está sendo ativado."); navigate({ to: "/painel", search: {} }); } }, [waitingConfirm, agent, navigate]); if (authLoading || !user) { return (
); } return (
{/* Header */}
{step === 1 && (

Bem-vindo à Mika! 🎉

Seu assistente pessoal de IA está sendo preparado.

    {[ { icon: "✅", text: "Pagamento confirmado" }, { icon: "✅", text: "Sua conta está ativa" }, { icon: "⏳", text: "Configurando seu agente..." }, ].map((item, i) => ( {item.icon} {item.text} ))}
)} {step === 2 && (

Como você quer chamar seu assistente?

Este será o nome que aparecerá nas conversas.

setAgentName(e.target.value)} placeholder="Ex: Mika de João, Maya, Assistente..." className="h-12 text-center text-lg bg-white/5 border-white/20 text-white placeholder:text-white/40 focus-visible:ring-primary" />
{agentName.trim().length < 2 ? "Mínimo 2 caracteres" : "\u00A0"} {agentName.length}/40
{[ `Mika de ${firstName}`, "Maya", "Alex", "Assistente", ].map((sugg) => ( ))}
)} {step === 3 && (

Crie seu bot em 1 toque 🚀

Preparamos tudo para você. Basta confirmar no Telegram.

{/* Card de preview do bot */}

{agentName || "Seu agente"}

@{previewUsername || "carregando…"}

{/* Estado: aguardando confirmação */} {waitingConfirm && (
Aguardando confirmação no Telegram…

Confirme as informações na conversa com @mika_managerbot.

)} {/* Estado: timeout */} {waitTimedOut && (

Não recebemos a confirmação ainda. Tente novamente ou faça o processo manualmente.

)} {/* Ícones explicativos — só antes de iniciar */} {!waitingConfirm && !waitTimedOut && (
)} {/* Ações */}
{!waitingConfirm && !waitTimedOut && ( )} {waitTimedOut && ( )}
)}
{ setWizardOpen(open); if (!open) { // Se o token foi salvo (vault_id presente), considera sucesso if (agent?.telegram_bot_token_vault_id) { toast.success( "Perfeito! Seu agente está sendo ativado. Você receberá uma mensagem no Telegram quando estiver pronto! 🎉", ); } navigate({ to: "/painel", search: {} }); } }} />
); } function StepRow({ number, title, hint, extra, }: { number: number; title: string; hint?: string; extra?: React.ReactNode; }) { return (
  • {number}

    {title}

    {hint &&

    {hint}

    } {extra}
  • ); } function ExplainIcon({ emoji, label }: { emoji: string; label: string }) { return (
    {emoji}

    {label}

    ); } // Marker no componente para cumprir contrato de "marcar como visitada" // (também usado pelo redirect do /painel) export function isWelcomeDone(): boolean { if (typeof window === "undefined") return false; return window.localStorage.getItem(WELCOME_DONE_KEY) === "1"; }