"use client"; import { createFileRoute, useNavigate, Link, useRouter } from "@tanstack/react-router"; import { useState, lazy, Suspense, useCallback, useMemo } from "react"; import { useMutation, useQueryClient } from "@tanstack/react-query"; import { ArrowLeft, Eye, FileText, Loader2, Play, Save } from "lucide-react"; import { toast } from "sonner"; import ReactMarkdown from "react-markdown"; import remarkGfm from "remark-gfm"; import { supabase } from "@/integrations/supabase/client"; import { useAuth } from "@/hooks/use-auth"; import { useAgentInstance } from "@/hooks/use-agent-instance"; import { Button } from "@/components/ui/button"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { SkillTestPanel } from "@/components/mika/skills/SkillTestPanel"; // Lazy-load CodeMirror to reduce initial bundle const CodeMirrorEditor = lazy(() => import("@/components/mika/skills/SkillMarkdownEditor")); export const Route = createFileRoute("/painel/skills/preview")({ component: SkillPreviewPage, }); function SkillPreviewPage() { const router = useRouter(); const navigate = useNavigate(); const { user } = useAuth(); const agent = useAgentInstance(); const qc = useQueryClient(); // State passed from /painel/skills/nova const routerState = (router.state.location.state ?? {}) as { markdown_content?: string; form_inputs?: Record; }; const [markdown, setMarkdown] = useState(routerState.markdown_content ?? ""); const formInputs = useMemo(() => routerState.form_inputs ?? {}, [routerState.form_inputs]); const [testOpen, setTestOpen] = useState(false); const [saving, setSaving] = useState(false); const [publishing, setPublishing] = useState(false); const agentId = agent.data?.id; const hasContent = markdown.trim().length > 0; // Helper: parse Supabase error codes const handleSupabaseError = useCallback((error: { code?: string; message?: string }) => { if (error.code === "P0001") { toast.error("Você precisa de uma assinatura ativa para criar skills.", { action: { label: "Ver planos", onClick: () => navigate({ to: "/", hash: "planos" }) }, }); return true; } if (error.code === "P0002") { toast.error("Você atingiu o limite de skills do seu plano. Faça upgrade ou arquive uma skill existente."); return true; } if (error.code === "23505") { toast.error("Você já tem uma skill com esse nome. Escolha outro."); return true; } return false; }, [navigate]); const createSkill = useCallback(async (publish: boolean) => { if (!user || !agentId) return; if (markdown.length > 50000) { toast.error("O conteúdo excede 50.000 caracteres. Reduza antes de salvar."); return; } const setter = publish ? setPublishing : setSaving; setter(true); try { const fi = formInputs as Record; // 1. Create skill const { data: skill, error: skillErr } = await supabase .from("skills") .insert({ user_id: user.id, agent_instance_id: agentId, name: (fi.name as string) || "Skill sem nome", description: (fi.description as string) || "", trigger_keywords: (fi.trigger_keywords as string) || "", status: "draft", }) .select("id") .single(); if (skillErr) { if (!handleSupabaseError(skillErr as { code?: string })) { toast.error(skillErr.message || "Erro ao criar skill"); } return; } // 2. Create version const { data: ver, error: verErr } = await supabase .from("skill_versions") .insert({ skill_id: skill.id, version_number: 1, markdown_content: markdown, form_inputs: formInputs as Record, is_live: false, created_by: user.id, }) .select("id") .single(); if (verErr) { toast.error(verErr.message || "Erro ao salvar versão"); return; } // 3. Optionally publish if (publish) { const { data: pubData, error: pubErr } = await supabase.functions.invoke( "publish-skill-version", { body: { skill_version_id: ver.id } }, ); if (pubErr) { toast.error("Skill salva, mas falha ao publicar: " + pubErr.message); } else { toast.success("Skill publicada com sucesso!"); } } else { toast.success("Rascunho salvo!"); } qc.invalidateQueries({ queryKey: ["skills"] }); qc.invalidateQueries({ queryKey: ["user-limits"] }); navigate({ to: "/painel/skills/$id", params: { id: skill.id } }); } catch { toast.error("Erro inesperado"); } finally { setter(false); } }, [user, agentId, markdown, formInputs, handleSupabaseError, navigate, qc]); if (!routerState.markdown_content) { return (

Nenhum conteúdo para pré-visualizar.

); } return (

Pré-visualização

{/* Desktop: split | Mobile: tabs */}
}>
Editor Preview
}>
{testOpen && ( ).name || "Skill"} skillVersionId="preview" triggerKeywords={(formInputs as Record).trigger_keywords} stateless={{ markdown_content: markdown }} /> )}
); } function MarkdownPreview({ content }: { content: string }) { return (
{content}
); } function EditorSkeleton() { return (
); }