import { useEffect, useMemo, useState } from 'react'; import { DataPanel } from './DataPanel'; import { createBotFlowNode, deleteBotFlowNode, getBotFlow, listBotFlowVersions, publishBotFlow, updateBotFlowNode, } from '../services/knowledgeService'; const fieldStyle = { width: '100%', border: '1px solid var(--color-border)', borderRadius: 14, padding: '0.78rem 0.9rem', background: '#fff', color: 'var(--color-text)', fontWeight: 600, }; const primaryButton = { border: 'none', borderRadius: 14, padding: '0.78rem 1rem', background: 'var(--color-primary)', color: '#fff', fontWeight: 800, }; const ghostButton = { border: '1px solid var(--color-border)', borderRadius: 14, padding: '0.72rem 0.9rem', background: '#fff', color: 'var(--color-text)', fontWeight: 800, }; const emptyDraft = { nodeType: 'question', title: '', messageText: '', keywords: '', fallbackMessage: '', fallbackAttempts: 2, fallbackAreaId: '', areaId: '', }; const closeDefaultMessage = 'Perfeito, vou encerrar por aqui. Se precisar de algo mais, é só chamar novamente.'; function nodeTypeLabel(type) { if (type === 'greeting') return 'Saudação'; if (type === 'agent') return 'Enviar para agente'; if (type === 'close') return 'Encerrar pelo bot'; return 'Pergunta'; } function splitKeywords(value) { return String(value || '') .split(',') .map((keyword) => keyword.trim()) .filter(Boolean); } function collectPublishWarnings(node, warnings = []) { if (!node) return warnings; const children = node.children || []; const isTerminal = node.node_type === 'agent' || node.node_type === 'close'; if (!isTerminal && children.length === 0) { warnings.push(`"${node.title}" precisa ter pelo menos um filho.`); } if (node.node_type === 'agent' && !node.area_id) { warnings.push(`"${node.title}" precisa de uma especialidade.`); } children.forEach((child) => collectPublishWarnings(child, warnings)); return warnings; } function WhatsAppPreview({ message }) { return (
Preview WhatsApp
{message || 'Digite a mensagem para visualizar aqui.'}
); } function FlowNode({ node, areasById, onAdd, onEdit, onDelete }) { const keywords = splitKeywords(node.keywords); const isRoot = node.node_type === 'greeting'; const isAgent = node.node_type === 'agent'; const isClose = node.node_type === 'close'; return (
{nodeTypeLabel(node.node_type)} {node.title}
{!isAgent && !isClose && onAdd ? ( ) : null}
{isAgent ? ( Fila: {node.area_nome || areasById.get(Number(node.area_id))?.nome || 'não definida'} ) : isClose ? ( {node.message_text || 'Fecha o atendimento sem enviar para agente.'} ) : ( {node.message_text || 'Sem mensagem configurada.'} )} {!isRoot && keywords.length ? (
{keywords.slice(0, 8).map((keyword) => ( {keyword} ))}
) : null}
{onEdit ? ( ) : null} {!isRoot && onDelete ? ( ) : null}
{node.children?.length ? ( <>
{node.children.map((child) => ( ))}
) : null}
); } function NodeModal({ mode, node, parent, areas, draft, onDraftChange, onClose, onSave }) { if (!mode) return null; const isEdit = mode === 'edit'; const isRoot = node?.node_type === 'greeting'; const isAgent = draft.nodeType === 'agent' || node?.node_type === 'agent'; const isClose = draft.nodeType === 'close' || node?.node_type === 'close'; const canChooseType = !isEdit; function change(key, value) { onDraftChange((current) => ({ ...current, [key]: value })); } return (

{isEdit ? 'Editar nó' : `Adicionar filho em ${parent?.title}`}

Configure a mensagem, as palavras que ativam o caminho e o destino quando for terminal.

{canChooseType ? (
{[ ['question', 'Adicionar pergunta'], ['agent', 'Enviar para agente'], ['close', 'Encerrar pelo bot'], ].map(([type, label]) => ( ))}
) : null}
{!isRoot ? ( ) : null} {isAgent ? ( ) : isClose ? ( <>