import { useState, useCallback } from "react"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Badge } from "@/components/ui/badge"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Textarea } from "@/components/ui/textarea"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Plus, Play, Pause, Trash2, Settings, GitBranch, Mail, MessageSquare, Database, Clock, Zap, FileText, Users, Bell, ArrowRight, Save, CheckCircle, XCircle, AlertTriangle } from "lucide-react"; interface WorkflowNode { id: string; type: "trigger" | "action" | "condition" | "delay"; name: string; config: Record; position: { x: number; y: number }; nextNodes: string[]; } interface Workflow { id: number; name: string; description: string; status: "active" | "inactive" | "draft"; nodes: WorkflowNode[]; createdAt: string; } const nodeTypes = { triggers: [ { type: "trigger", subtype: "new_record", name: "Novo Registro", icon: Database, color: "bg-green-500" }, { type: "trigger", subtype: "schedule", name: "Agendamento", icon: Clock, color: "bg-blue-500" }, { type: "trigger", subtype: "webhook", name: "Webhook", icon: Zap, color: "bg-purple-500" }, { type: "trigger", subtype: "form_submit", name: "Formulário Enviado", icon: FileText, color: "bg-orange-500" }, ], actions: [ { type: "action", subtype: "send_email", name: "Enviar Email", icon: Mail, color: "bg-red-500" }, { type: "action", subtype: "send_whatsapp", name: "Enviar WhatsApp", icon: MessageSquare, color: "bg-green-600" }, { type: "action", subtype: "create_record", name: "Criar Registro", icon: Database, color: "bg-blue-600" }, { type: "action", subtype: "update_record", name: "Atualizar Registro", icon: Database, color: "bg-indigo-500" }, { type: "action", subtype: "notify", name: "Notificar Usuário", icon: Bell, color: "bg-yellow-500" }, { type: "action", subtype: "assign_user", name: "Atribuir a Usuário", icon: Users, color: "bg-pink-500" }, ], conditions: [ { type: "condition", subtype: "if_else", name: "Se/Senão", icon: GitBranch, color: "bg-gray-500" }, ], delays: [ { type: "delay", subtype: "wait", name: "Aguardar", icon: Clock, color: "bg-gray-400" }, ] }; export default function WorkflowBuilder() { const queryClient = useQueryClient(); const [selectedWorkflow, setSelectedWorkflow] = useState(null); const [nodes, setNodes] = useState([]); const [selectedNode, setSelectedNode] = useState(null); const [showNewDialog, setShowNewDialog] = useState(false); const [newWorkflow, setNewWorkflow] = useState({ name: "", description: "" }); const [draggedNode, setDraggedNode] = useState(null); const { data: workflows = [] } = useQuery({ queryKey: ["/api/lowcode/workflows"], queryFn: async () => { const res = await fetch("/api/lowcode/workflows"); if (!res.ok) return []; return res.json(); } }); const createWorkflow = useMutation({ mutationFn: async (data: { name: string; description: string }) => { const res = await fetch("/api/lowcode/workflows", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ ...data, nodes: [], status: "draft" }) }); return res.json(); }, onSuccess: (data) => { queryClient.invalidateQueries({ queryKey: ["/api/lowcode/workflows"] }); setSelectedWorkflow(data); setNodes([]); setShowNewDialog(false); setNewWorkflow({ name: "", description: "" }); } }); const saveWorkflow = useMutation({ mutationFn: async () => { if (!selectedWorkflow) return; const res = await fetch(`/api/lowcode/workflows/${selectedWorkflow.id}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ nodes }) }); return res.json(); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["/api/lowcode/workflows"] }); } }); const handleDragStart = (e: React.DragEvent, nodeType: any) => { setDraggedNode(nodeType); e.dataTransfer.effectAllowed = "copy"; }; const handleDrop = (e: React.DragEvent) => { e.preventDefault(); if (!draggedNode) return; const canvas = e.currentTarget.getBoundingClientRect(); const x = e.clientX - canvas.left; const y = e.clientY - canvas.top; const newNode: WorkflowNode = { id: `node_${Date.now()}`, type: draggedNode.type, name: draggedNode.name, config: { subtype: draggedNode.subtype }, position: { x, y }, nextNodes: [] }; setNodes([...nodes, newNode]); setDraggedNode(null); }; const handleDragOver = (e: React.DragEvent) => { e.preventDefault(); e.dataTransfer.dropEffect = "copy"; }; const deleteNode = (nodeId: string) => { setNodes(nodes.filter(n => n.id !== nodeId)); if (selectedNode?.id === nodeId) setSelectedNode(null); }; const getNodeIcon = (node: WorkflowNode) => { const allNodes = [...nodeTypes.triggers, ...nodeTypes.actions, ...nodeTypes.conditions, ...nodeTypes.delays]; const found = allNodes.find(n => n.subtype === node.config.subtype); return found || { icon: Zap, color: "bg-gray-500" }; }; return (

Workflow Builder

Automações visuais

Criar Workflow
setNewWorkflow({ ...newWorkflow, name: e.target.value })} placeholder="Nome do workflow" data-testid="input-workflow-name" />