diff --git a/src/routes/bem-vindo.tsx b/src/routes/bem-vindo.tsx new file mode 100644 index 0000000..efb495a --- /dev/null +++ b/src/routes/bem-vindo.tsx @@ -0,0 +1,401 @@ +"use client"; + +import { createFileRoute, useNavigate } from "@tanstack/react-router"; +import { useEffect, useMemo, useState } from "react"; +import { motion, AnimatePresence } from "framer-motion"; +import { + ArrowRight, + CheckCircle2, + ExternalLink, + Loader2, + 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); + + 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 handleConnectTelegram() { + await markWelcomeDone(); + setWizardOpen(true); + } + + async function handleSkip() { + await markWelcomeDone(); + navigate({ to: "/painel", search: {} }); + } + + 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 && ( + +

+ Quase lá! Conecte seu Telegram. +

+

+ Para conversar com{" "} + + {agentName || "seu agente"} + + , você precisa conectar seu Telegram. Leva menos de 2 minutos. +

+ + + + + +
    + + + Abrir BotFather + + + } + /> + + +
+ +
+ + +
+
+ )} +
+
+
+ + { + setWizardOpen(open); + // Quando fechar o wizard, leva ao painel + if (!open) { + 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} +
    +
  • + ); +} + +// 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"; +}