Problem Statement
Operadores que utilizam o painel ISing para atendimento via WhatsApp enfrentam duas categorias de falhas que tornam o sistema inutilizável em produção:
Módulo de Chat: Mensagens enviadas pelo painel não aparecem na conversa, mensagens recebidas dos contatos não são refletidas em tempo real mesmo com o WebSocket ativo, e o alinhamento visual dos balões está incorreto — dificultando a distinção entre mensagens enviadas e recebidas. O resultado é que o operador não consegue realizar atendimentos de forma confiável.
Módulo de Instâncias: O fluxo de criação de instâncias apresenta falhas que confundem o usuário sobre o estado real da conexão. A exclusão de instâncias não possui confirmação, tornando a ação irreversível sem aviso prévio.
Solution
Corrigir o pipeline de mensagens (envio, recebimento em tempo real, recuperação pós-reconexão e alinhamento visual) e completar o ciclo de vida das instâncias (fluxo de QR Code robusto, exibição de foto de perfil e exclusão com confirmação).
User Stories
Chat
- Como operador, quero digitar uma mensagem no painel e enviá-la ao contato, para que a conversa avance sem precisar usar o WhatsApp diretamente.
- Como operador, quero que a mensagem que acabei de enviar apareça imediatamente na janela de chat, para que eu tenha confirmação visual do envio sem precisar recarregar a página.
- Como operador, quero que uma mensagem recebida do contato apareça na janela de chat em tempo real, para que eu possa responder sem atrasos.
- Como operador, quero ver as mensagens que enviei alinhadas à direita e as mensagens do contato alinhadas à esquerda, para que eu identifique visualmente quem escreveu cada mensagem.
- Como operador, quero que, ao reconectar o WebSocket após uma queda, as mensagens recebidas durante o período offline apareçam automaticamente, para que eu não perca o contexto da conversa.
- Como operador, quero receber uma notificação de erro visível quando o envio de uma mensagem falhar, para que eu saiba que preciso tentar novamente em vez de achar que a mensagem foi enviada.
- Como operador, quero que uma nova conversa iniciada por um contato apareça automaticamente na lista lateral, para que eu não precise recarregar a página para atender clientes novos.
- Como operador, quero que as mensagens de uma conversa apareçam apenas naquela conversa, para que não haja mistura de mensagens entre atendimentos distintos.
- Como operador, quero que o histórico de mensagens carregue corretamente ao selecionar uma conversa, para que eu tenha contexto do que já foi discutido com o contato.
- Como operador, quero que as mensagens de imagem, áudio e documento sejam exibidas corretamente na janela de chat, para que eu visualize todo o conteúdo trocado com o contato.
Instâncias — Fluxo de QR Code
- Como administrador, quero que ao criar uma nova instância o sistema não a marque imediatamente como conectada, para que o status reflita a realidade (aguardando QR Code).
- Como administrador, quero que após criar uma instância o sistema aguarde 5 segundos e então exiba o QR Code em um modal, para que eu possa escanear com o celular e realizar a conexão.
- Como administrador, quero ver um indicador de carregamento durante os 5 segundos de espera antes do QR Code aparecer, para que eu entenda que o sistema está processando.
- Como administrador, quero que o QR Code seja atualizado automaticamente a cada 30 segundos caso não seja escaneado, para que o código não expire durante o processo de conexão.
- Como administrador, quero que se eu fechar o modal sem escanear o QR Code, a instância apareça na lista com status "Desconectado", para que eu saiba que a conexão ainda não foi estabelecida.
- Como administrador, quero que no card de uma instância desconectada exista um botão "Gerar QR", para que eu possa reabrir o modal e tentar conectar novamente sem precisar excluir e recriar a instância.
- Como administrador, quero que após escanear o QR Code e a conexão ser estabelecida, o modal exiba uma mensagem de sucesso e o card da instância atualize para "Conectado", para que eu tenha confirmação visual imediata.
- Como administrador, quero que o status da instância seja consultado a cada 5 segundos durante o processo de conexão, para que o painel reflita o momento exato em que o WhatsApp conecta.
Instâncias — Foto de Perfil
- Como administrador, quero ver a foto de perfil do WhatsApp associada à instância diretamente no card, para que eu identifique visualmente qual número está conectado sem precisar abrir configurações.
- Como administrador, quero que a foto de perfil seja buscada automaticamente do campo
profilePicture retornado pelo endpoint de status, para que não seja necessário nenhuma ação manual após a conexão.
- Como administrador, quero que caso a foto de perfil esteja indisponível (nula ou URL inválida), o card exiba um avatar padrão sem erros visuais, para que a interface permaneça consistente.
- Como administrador, quero que a foto de perfil seja atualizada sempre que o status da instância for consultado, para que uma troca de foto no WhatsApp seja refletida no painel.
Instâncias — Exclusão
- Como administrador, quero que ao clicar no botão de excluir uma instância, um modal de confirmação seja exibido antes da exclusão acontecer, para que eu não exclua uma instância acidentalmente.
- Como administrador, quero que o modal de confirmação de exclusão informe claramente que a ação é irreversível, para que eu tenha ciência das consequências antes de confirmar.
- Como administrador, quero que ao confirmar a exclusão, a instância seja removida do gateway Pastorini e da listagem do painel, para que não existam instâncias órfãs no sistema.
- Como administrador, quero que ao cancelar o modal de confirmação de exclusão, nenhuma ação seja tomada e eu retorne ao estado anterior, para que falsos cliques não causem exclusões indesejadas.
- Como administrador, quero que enquanto a exclusão está em andamento, o botão de confirmar fique desabilitado e mostre um indicador de carregamento, para que eu não submeta a ação duas vezes.
Implementation Decisions
Chat — Módulo de Mensagens
- Store de mensagens (
addMessage): A função deve filtrar mensagens pelo ID da conversa ativa antes de adicioná-las ao array de mensagens da tela. Mensagens de outras conversas devem apenas atualizar os metadados da conversa na lista (unread_count, last_message_at) sem aparecer na janela aberta. A filtragem deve usar conversation_id quando disponível no payload do WebSocket.
- Tipo
Message no frontend: Adicionar os campos conversation_id e contact_id ao tipo, pois o backend já os retorna. Isso permite filtragem precisa no store.
- Stale closure no store: O terceiro
set de reordenação da lista de conversas deve ser consolidado com o segundo set de atualização de metadados em um único set funcional para evitar sobrescrever mudanças de estado intermediárias.
- Envio com erro silencioso (
handleSend): O componente de janela de chat deve fazer await no sendMessage e capturar erros com try/catch, exibindo um toast de erro para o operador quando o envio falhar.
- Recuperação pós-reconexão do WebSocket: A página de chat deve detectar quando o WebSocket transita de desconectado para conectado e disparar automaticamente
fetchConversations e fetchMessages para cobrir o gap de mensagens recebidas durante a queda.
- Novas conversas via webhook: Quando o evento
new_message chegar via WebSocket para uma conversa que não está na lista local, o painel deve disparar fetchConversations para incluir o novo atendimento.
- Alinhamento dos balões: Determinar lado do balão exclusivamente pelo campo
direction do payload (outbound → direita, inbound → esquerda). O campo é canonicamente definido pelo backend e não deve ser inferido pelo frontend.
Instâncias — QR Code
- Status inicial ao criar: Ao criar uma instância, o status deve permanecer
connecting enquanto o gateway processa. O frontend não deve inferir connected antes de receber confirmação via polling.
- Fluxo do modal de QR: Após o POST de criação, o frontend aguarda 5 segundos (com contador visual), chama
GET /instances/{id}/qr e exibe o base64 retornado no campo qrImage (ou equivalente, conforme normalização já existente no cliente Pastorini).
- Botão no card desconectado: Instâncias com status
disconnected devem exibir um botão "Gerar QR" que abre o mesmo modal e executa GET /instances/{id}/qr sem o delay de 5 segundos (instância já existe no gateway).
- Auto-refresh de 30 segundos: O modal deve renovar o QR automaticamente enquanto o status for
qr_ready, exibindo countdown visual ao usuário.
- Polling de status: Durante a fase de conexão, o frontend faz polling de
GET /instances/{id}/status a cada 5 segundos para detectar a transição qr_ready → connected.
- Fechamento do modal: Ao fechar o modal sem escanear, o painel não altera o status da instância — o status real vem apenas do polling ou do webhook de conexão.
Instâncias — Foto de Perfil
- Fonte dos dados: O campo
profile_picture_url deve ser extraído do retorno de GET /instances/{id}/status, que por sua vez lê do Pastorini. O cliente Pastorini já normaliza os campos profilePicture, profilePictureUrl e avatarUrl.
- Hidratação automática: Instâncias com status
connected devem ter a foto de perfil buscada automaticamente no carregamento da página de instâncias e após cada polling bem-sucedido de status.
- Fallback visual: Caso
profile_picture_url seja nulo, vazio ou resulte em erro de carregamento de imagem, exibir ícone padrão (ex: ícone de smartphone) sem quebrar o layout.
Instâncias — Exclusão com Confirmação
- Modal de confirmação: Adicionar um componente de diálogo de confirmação genérico (ou reutilizar um existente) que exiba: título "Excluir instância", corpo "Tem certeza que deseja excluir esta instância? Esta ação não pode ser desfeita." e botões "Cancelar" / "Excluir".
- Estado de loading no botão: Enquanto a requisição
DELETE /instances/{id} estiver em andamento, o botão de confirmar deve estar desabilitado e exibir spinner.
- Remoção da lista: Após exclusão bem-sucedida, remover o item do array de instâncias no store sem recarregar a lista completa.
- Endpoint backend:
DELETE /instances/{id} já existe no backend com lógica de auditoria e chamada ao gateway Pastorini. Não requer alteração de backend.
Contratos de API (referência)
| Ação |
Método |
Endpoint (Pastorini) |
Campo relevante |
| Criar instância |
POST |
/api/instances |
— |
| Obter QR Code |
GET |
/api/instances/{instance}/qr |
qrImage (base64) |
| Status / Perfil |
GET |
/api/instances/{instance}/status |
profilePicture, status |
| Excluir instância |
DELETE |
/api/instances/{instance} |
— |
Testing Decisions
Princípio geral: Testes devem verificar comportamento externo observável — o que o usuário/sistema vê — e não detalhes de implementação interna como chamadas a funções específicas ou estrutura de estado.
Módulo de Chat — Store de Mensagens
- Testar que
addMessage com uma mensagem de conversa diferente da ativa não adiciona ao array de mensagens renderizadas
- Testar que
addMessage com mensagem da conversa ativa adiciona ao array corretamente
- Testar que deduplicação por
id previne mensagens duplicadas
- Testar que a reordenação da lista de conversas após nova mensagem está correta sem sobrescrever
unread_count
Módulo de Instâncias
- Testar que o modal de confirmação de exclusão é exibido antes de qualquer chamada de API
- Testar que ao confirmar exclusão, a instância é removida da lista
- Testar que ao cancelar, a lista permanece intacta
- Testar que foto de perfil nula renderiza avatar padrão sem erro
Out of Scope
- Implementação de funcionalidades de Typebot, Chatwoot ou AI Agent nas instâncias
- Upload manual de foto de perfil pelo administrador
- Operações em lote de instâncias (seleção múltipla)
- Renomeação de instâncias após criação
- Configuração de webhook via interface de configurações (marcada como "em migração")
- Envio de mensagens de mídia (imagem, áudio, documento) pelo painel — apenas texto está no escopo atual
- Implementação de autenticação pública de instâncias
Further Notes
- O cliente Pastorini já possui normalização de campos para
qrImage, profilePicture e mapeamento de status (20+ variantes mapeadas). O frontend deve confiar nessa normalização ao invés de implementar parsers próprios.
- A variável
API_DOMAIN=localhost:8010 no .env de desenvolvimento aponta para o backend local. Para que webhooks do Pastorini (servidor remoto em 187.77.55.218:3001) cheguem ao backend, é necessário expor o backend publicamente em ambiente de testes (ngrok, tunnel, etc.) — caso contrário, mensagens inbound nunca alcançarão o backend mesmo com o código correto.
- A rota
DELETE /instances/{id} já está implementada no backend e requer role ADMIN. O frontend deve garantir que o botão de exclusão apareça apenas para usuários com essa role.
Problem Statement
Operadores que utilizam o painel ISing para atendimento via WhatsApp enfrentam duas categorias de falhas que tornam o sistema inutilizável em produção:
Módulo de Chat: Mensagens enviadas pelo painel não aparecem na conversa, mensagens recebidas dos contatos não são refletidas em tempo real mesmo com o WebSocket ativo, e o alinhamento visual dos balões está incorreto — dificultando a distinção entre mensagens enviadas e recebidas. O resultado é que o operador não consegue realizar atendimentos de forma confiável.
Módulo de Instâncias: O fluxo de criação de instâncias apresenta falhas que confundem o usuário sobre o estado real da conexão. A exclusão de instâncias não possui confirmação, tornando a ação irreversível sem aviso prévio.
Solution
Corrigir o pipeline de mensagens (envio, recebimento em tempo real, recuperação pós-reconexão e alinhamento visual) e completar o ciclo de vida das instâncias (fluxo de QR Code robusto, exibição de foto de perfil e exclusão com confirmação).
User Stories
Chat
Instâncias — Fluxo de QR Code
Instâncias — Foto de Perfil
profilePictureretornado pelo endpoint de status, para que não seja necessário nenhuma ação manual após a conexão.Instâncias — Exclusão
Implementation Decisions
Chat — Módulo de Mensagens
addMessage): A função deve filtrar mensagens pelo ID da conversa ativa antes de adicioná-las ao array de mensagens da tela. Mensagens de outras conversas devem apenas atualizar os metadados da conversa na lista (unread_count, last_message_at) sem aparecer na janela aberta. A filtragem deve usarconversation_idquando disponível no payload do WebSocket.Messageno frontend: Adicionar os camposconversation_idecontact_idao tipo, pois o backend já os retorna. Isso permite filtragem precisa no store.setde reordenação da lista de conversas deve ser consolidado com o segundosetde atualização de metadados em um únicosetfuncional para evitar sobrescrever mudanças de estado intermediárias.handleSend): O componente de janela de chat deve fazerawaitnosendMessagee capturar erros comtry/catch, exibindo um toast de erro para o operador quando o envio falhar.fetchConversationsefetchMessagespara cobrir o gap de mensagens recebidas durante a queda.new_messagechegar via WebSocket para uma conversa que não está na lista local, o painel deve dispararfetchConversationspara incluir o novo atendimento.directiondo payload (outbound→ direita,inbound→ esquerda). O campo é canonicamente definido pelo backend e não deve ser inferido pelo frontend.Instâncias — QR Code
connectingenquanto o gateway processa. O frontend não deve inferirconnectedantes de receber confirmação via polling.GET /instances/{id}/qre exibe o base64 retornado no campoqrImage(ou equivalente, conforme normalização já existente no cliente Pastorini).disconnecteddevem exibir um botão "Gerar QR" que abre o mesmo modal e executaGET /instances/{id}/qrsem o delay de 5 segundos (instância já existe no gateway).qr_ready, exibindo countdown visual ao usuário.GET /instances/{id}/statusa cada 5 segundos para detectar a transiçãoqr_ready → connected.Instâncias — Foto de Perfil
profile_picture_urldeve ser extraído do retorno deGET /instances/{id}/status, que por sua vez lê do Pastorini. O cliente Pastorini já normaliza os camposprofilePicture,profilePictureUrleavatarUrl.connecteddevem ter a foto de perfil buscada automaticamente no carregamento da página de instâncias e após cada polling bem-sucedido de status.profile_picture_urlseja nulo, vazio ou resulte em erro de carregamento de imagem, exibir ícone padrão (ex: ícone de smartphone) sem quebrar o layout.Instâncias — Exclusão com Confirmação
DELETE /instances/{id}estiver em andamento, o botão de confirmar deve estar desabilitado e exibir spinner.DELETE /instances/{id}já existe no backend com lógica de auditoria e chamada ao gateway Pastorini. Não requer alteração de backend.Contratos de API (referência)
/api/instances/api/instances/{instance}/qrqrImage(base64)/api/instances/{instance}/statusprofilePicture,status/api/instances/{instance}Testing Decisions
Princípio geral: Testes devem verificar comportamento externo observável — o que o usuário/sistema vê — e não detalhes de implementação interna como chamadas a funções específicas ou estrutura de estado.
Módulo de Chat — Store de Mensagens
addMessagecom uma mensagem de conversa diferente da ativa não adiciona ao array de mensagens renderizadasaddMessagecom mensagem da conversa ativa adiciona ao array corretamenteidprevine mensagens duplicadasunread_countMódulo de Instâncias
Out of Scope
Further Notes
qrImage,profilePicturee mapeamento de status (20+ variantes mapeadas). O frontend deve confiar nessa normalização ao invés de implementar parsers próprios.API_DOMAIN=localhost:8010no.envde desenvolvimento aponta para o backend local. Para que webhooks do Pastorini (servidor remoto em187.77.55.218:3001) cheguem ao backend, é necessário expor o backend publicamente em ambiente de testes (ngrok, tunnel, etc.) — caso contrário, mensagens inbound nunca alcançarão o backend mesmo com o código correto.DELETE /instances/{id}já está implementada no backend e requer roleADMIN. O frontend deve garantir que o botão de exclusão apareça apenas para usuários com essa role.