(name, {
+ body: body ?? {},
+ });
+
+ if (error) {
+ // Tenta extrair mensagem do response real
+ let msg = error.message ?? "Erro inesperado";
+ let status: number | undefined;
+ // deno-lint-ignore no-explicit-any
+ const ctx = (error as any).context as Response | undefined;
+ if (ctx && typeof ctx.json === "function") {
+ try {
+ status = ctx.status;
+ const parsed = await ctx.json();
+ if (parsed?.error) msg = parsed.error;
+ } catch {
+ // ignora
+ }
+ }
+ return { data: null, error: { message: msg, status } };
+ }
+
+ return { data: (data ?? null) as T | null, error: null };
+ } catch (err) {
+ return {
+ data: null,
+ error: {
+ message: err instanceof Error ? err.message : "Erro inesperado",
+ },
+ };
+ }
+}
diff --git a/src/lib/telegram-username.ts b/src/lib/telegram-username.ts
new file mode 100644
index 0000000..2f802a8
--- /dev/null
+++ b/src/lib/telegram-username.ts
@@ -0,0 +1,25 @@
+"use client";
+
+/**
+ * Sanitiza um primeiro nome para uso em sugestões de username do Telegram.
+ * - remove acentos via NFD
+ * - remove caracteres não-alfanuméricos
+ * - lowercase
+ */
+export function sanitizeForUsername(name: string): string {
+ return (name || "")
+ .normalize("NFD")
+ .replace(/[\u0300-\u036f]/g, "")
+ .replace(/[^a-zA-Z0-9]/g, "")
+ .toLowerCase();
+}
+
+export function suggestBotName(fullName: string | null | undefined): string {
+ const first = (fullName || "").trim().split(/\s+/)[0] || "Você";
+ return `Mika de ${first}`;
+}
+
+export function suggestBotUsername(fullName: string | null | undefined): string {
+ const first = sanitizeForUsername((fullName || "").trim().split(/\s+/)[0] || "voce");
+ return `mika_${first || "voce"}_bot`;
+}
diff --git a/src/routes/painel.agente.tsx b/src/routes/painel.agente.tsx
index 8adde1a..30d48d8 100644
--- a/src/routes/painel.agente.tsx
+++ b/src/routes/painel.agente.tsx
@@ -1,7 +1,7 @@
"use client";
-import { createFileRoute, Link } from "@tanstack/react-router";
-import { Bot, Cpu, MessageSquare, Sparkles, BarChart3, Loader2 } from "lucide-react";
+import { createFileRoute } from "@tanstack/react-router";
+import { Bot, Cpu, BarChart3 } from "lucide-react";
import { useProfile } from "@/hooks/use-profile";
import { useAgentInstance } from "@/hooks/use-agent-instance";
import { useUserSkillLimits } from "@/hooks/use-user-skill-limits";
@@ -9,13 +9,8 @@ import { useQuery } from "@tanstack/react-query";
import { supabase } from "@/integrations/supabase/client";
import { useAuth } from "@/hooks/use-auth";
import { Badge } from "@/components/ui/badge";
-import { Button } from "@/components/ui/button";
import { Skeleton } from "@/components/ui/skeleton";
-import {
- Tooltip,
- TooltipContent,
- TooltipTrigger,
-} from "@/components/ui/tooltip";
+import { TelegramStatusCard } from "@/components/mika/telegram/TelegramStatusCard";
import { formatDistanceToNow } from "date-fns";
import { ptBR } from "date-fns/locale";
import { cn } from "@/lib/utils";
@@ -121,26 +116,8 @@ function AgentePage() {
- {/* Card 3: Canais conectados */}
-
-
-
-
Canais conectados
-
-
-
- Telegram
-
-
-
-
-
- Disponível em breve
-
-
-
+ {/* Card 3: Telegram */}
+
{/* Card 4: Estatísticas */}
diff --git a/src/routes/painel.index.tsx b/src/routes/painel.index.tsx
index 46d243b..470b07b 100644
--- a/src/routes/painel.index.tsx
+++ b/src/routes/painel.index.tsx
@@ -1,22 +1,50 @@
"use client";
import { createFileRoute, Link } from "@tanstack/react-router";
+import { useEffect, useState } from "react";
import { ArrowRight, CheckCircle2, Loader2, Sparkles } from "lucide-react";
import { useSubscription } from "@/hooks/use-profile";
import { useProfile } from "@/hooks/use-profile";
+import { useAgentInstance } from "@/hooks/use-agent-instance";
import { Button } from "@/components/ui/button";
import { Skeleton } from "@/components/ui/skeleton";
import { SubscriptionBanner } from "@/components/mika/SubscriptionBanner";
import { SkillsDashboardWidget } from "@/components/mika/SkillsDashboardWidget";
+import { TelegramOnboardingWizard } from "@/components/mika/telegram/TelegramOnboardingWizard";
+import { toast } from "sonner";
import { cn } from "@/lib/utils";
export const Route = createFileRoute("/painel/")({
+ validateSearch: (search: Record) => ({
+ status: typeof search.status === "string" ? (search.status as string) : undefined,
+ }),
component: DashboardPage,
});
function DashboardPage() {
const { data: subscription, isLoading } = useSubscription();
const { data: profile } = useProfile();
+ const { data: agent } = useAgentInstance();
+ const search = Route.useSearch();
+ const navigate = Route.useNavigate();
+ const [wizardOpen, setWizardOpen] = useState(false);
+
+ // Auto-open do wizard ao voltar com ?status=success
+ useEffect(() => {
+ if (search.status !== "success" || !agent) return;
+ if (agent.status === "suspended" || agent.status === "error") {
+ navigate({ search: {}, replace: true });
+ return;
+ }
+ if (!agent.telegram_bot_username) setWizardOpen(true);
+ navigate({ search: {}, replace: true });
+ }, [search.status, agent, navigate]);
+
+ useEffect(() => {
+ if (search.status === "success" && !agent && !isLoading) {
+ toast.info("Seu agente ainda não está pronto. Aguarde o provisionamento.");
+ }
+ }, [search.status, agent, isLoading]);
if (isLoading) {
return (
@@ -49,6 +77,8 @@ function DashboardPage() {
)}
{subscription && subscription.status === "active" && }
+
+
);
}
diff --git a/src/routes/painel.tsx b/src/routes/painel.tsx
index 3dcbac1..60b3f75 100644
--- a/src/routes/painel.tsx
+++ b/src/routes/painel.tsx
@@ -23,6 +23,7 @@ const PADDLE_ENV = (import.meta.env.VITE_PADDLE_ENVIRONMENT as string) === "prod
import { Logo } from "@/components/mika/Logo";
import { PaymentIssueBanner } from "@/components/mika/PaymentIssueBanner";
import { CancellationScheduledBanner } from "@/components/mika/CancellationScheduledBanner";
+import { TelegramConnectionBanner } from "@/components/mika/telegram/TelegramConnectionBanner";
import { ThemeToggle } from "@/components/mika/ThemeToggle";
import { Button } from "@/components/ui/button";
import {
@@ -159,7 +160,8 @@ function PainelLayout() {
-