🔄 Exemplo Completo: Criação de Mensagem até Resposta Followup

🎯 Resumo Simples do Fluxo

O que acontece:

  1. Admin cria uma mensagem → Sistema gera automaticamente um “followup” (link de resposta)

  2. Usuário recebe notificação na aplicação

  3. Usuário clica na notificação/mensagem → Abre a mensagem completa dentro da aplicação

  4. Dentro da mensagem, o frontend busca e exibe os botões de possíveis respostas

  1. Usuário escolhe como responder:
  1. Sistema registra a resposta → Admin pode ver quem respondeu o quê

Como funciona o ID do Followup:


📋 Fluxo Completo com Endpoints Específicos

Este exemplo demonstra o fluxo completo desde a criação de uma mensagem até a resposta do usuário, focando nos endpoints de resposta por índice e resposta por tipo.


🚀 FASE 1: Criação da Mensagem com Followup

1.1 Criar Mensagem


POST /api/mensagens

Authorization: Bearer {token}

Content-Type: application/json

  

{

"tipo": 2,

"tipoMensagemId": 2,

"titulo": "Confirmação de Presença - Evento Esportivo",

"descricaoBreve": "Confirme sua presença no evento esportivo",

"conteudo": "<h2>Evento Esportivo</h2><p>Dia: 15/01/2025</p><p>Horário: 14h00</p><p>Local: Ginásio Principal</p>",

"requerConfirmacao": true,

"dataLimiteResposta": "2025-01-14T23:59:59",

"entidadeId": 1,

"meiosEnvio": ["PushNotification", "Inbox", "Email"],

"destinatarios": [

{

"destinatarioId": "usuario-123",

"tipoDestinatario": "usuario",

"nivelHierarquico": 5,

"nomeDestinatario": "João Silva",

"emailDestinatario": "joao.silva@email.com"

}

]

}

1.2 Resposta da API


{

"success": true,

"message": "Mensagem com follow-up criado e enviado com sucesso.",

"data": {

"id": 55,

"tipo": "Comunicado",

"tipoMensagemId": 2,

"titulo": "Confirmação de Presença - Evento Esportivo",

"conteudo": "<h2>Evento Esportivo</h2><p>Dia: 15/01/2025</p><p>Horário: 14h00</p><p>Local: Ginásio Principal</p>",

"status": "enviado",

"dataEnvio": "2025-01-08T10:30:00Z",

"followUpId": 36

}

}

1.3 Sistema Cria FollowUp Automaticamente

🔧 Processo Técnico Detalhado:


// 1. Backend recebe a mensagem criada

var  mensagemCriada = resultCriacao.Data; // Mensagem ID = 55

  

// 2. Para cada destinatário, cria um FollowUp individual

var  followUps = new  List<FollowUp>();

foreach (var  dest  in  request.Destinatarios)

{

var  followUp = new  FollowUp

{

// ⭐ ID é gerado automaticamente pelo banco (IDENTITY)

Id = 0, // Será preenchido pelo banco

MensagemId = mensagemCriada.Id, // 55

UsuarioId = dest.DestinatarioId, // "usuario-123"

Status = StatusFollowUp.Aguardando,

DataEnvio = DateTime.Now,

TipoMensagemId = request.TipoMensagemId, // 2

TipoRespostaId = request.TipoRespostaId  // null inicialmente

};

followUps.Add(followUp);

}

  

// 3. Insere todos os FollowUps no banco

_dbFollowUp.InserirVarios(followUps);

  

// 4. Após inserção, os IDs são preenchidos automaticamente

// followUp.Id agora tem o valor real (ex: 36)

📊 Como o ID é Usado:


// Após inserir no banco, o sistema obtém os IDs gerados

foreach (var  followUp  in  followUps)

{

// O ID agora está disponível: followUp.Id = 36

// 5. O ID é associado à mensagem para uso posterior

// Não precisamos mais gerar links HTML no conteúdo

// O frontend usará este ID para carregar os botões de resposta

}

🎯 Resultado Final:

📱 Como o Frontend Obtém o FollowUpId:


// 1. Quando a mensagem é carregada, o followUpId vem junto

const mensagem = {

id: 55,

titulo: "Confirmação de Presença - Evento Esportivo",

conteudo: "<h2>Evento Esportivo</h2><p>Dia: 15/01/2025</p>...",

followUpId: 36 // ⭐ ID do followup para este usuário

};

  

// 2. Frontend usa este ID para carregar botões de resposta

const followUpId = mensagem.followUpId; // 36

  

// 3. Carregar opções de resposta usando este ID

carregarOpcoesResposta(followUpId);


📱 FASE 2: Usuário Recebe Notificação na Aplicação

2.1 Notificação na Aplicação


<!-- Conteúdo da notificação na aplicação -->

<div class="notification-card" onclick="abrirMensagem(55, 36)">

<h3>📢 Novo Comunicado</h3>

<h4>Confirmação de Presença - Evento Esportivo</h4>

<p><strong>Dia:</strong> 15/01/2025</p>

<p><strong>Horário:</strong> 14h00</p>

<p><strong>Local:</strong> Ginásio Principal</p>

<div class="status">

<span class="badge">Aguardando Resposta</span>

</div>

</div>

2.2 Usuário Clica na Mensagem


// Função chamada quando usuário clica na notificação

function abrirMensagem(mensagemId, followUpId) {

// 1. Carregar conteúdo completo da mensagem

carregarMensagemCompleta(mensagemId);

// 2. Carregar opções de resposta para este followup

carregarOpcoesResposta(followUpId);

// 3. Exibir modal ou expandir seção da mensagem

mostrarMensagemComRespostas();

}

🔧 Como o Frontend Obtém o ID:


// 1. ID vem da notificação/mensagem recebida

const notificacao = {

mensagemId: 55,

followUpId: 36,

titulo: "Confirmação de Presença - Evento Esportivo",

status: "Aguardando"

};

  

// 2. Usar o ID diretamente da notificação

const followUpId = notificacao.followUpId; // 36

  

// 3. Carregar opções de resposta usando este ID

async function carregarOpcoesResposta(followUpId) {

const response = await fetch(`/api/follow-up/${followUpId}/respostas-possiveis`);

const data = await response.json();

// Renderizar botões de resposta na mensagem

renderizarBotoesResposta(data.respostas, followUpId);

}


🔍 FASE 3: Frontend Carrega Opções da Tabela 'tiporesposta’

3.1 Buscar Opções de Resposta


GET /api/follow-up/36/respostas-possiveis

Content-Type: application/json

3.2 Resposta da API (Dados da Tabela ‘tiporesposta’)


{

"respostas": [

{

"id": 1,

"tipoResposta": "Sim",

"criacao": "2025-01-01T00:00:00Z",

"atualizacao": "2025-01-01T00:00:00Z"

},

{

"id": 2,

"tipoResposta": "Não",

"criacao": "2025-01-01T00:00:00Z",

"atualizacao": "2025-01-01T00:00:00Z"

},

{

"id": 3,

"tipoResposta": "Talvez",

"criacao": "2025-01-01T00:00:00Z",

"atualizacao": "2025-01-01T00:00:00Z"

},

{

"id": 4,

"tipoResposta": "Confirmo",

"criacao": "2025-01-01T00:00:00Z",

"atualizacao": "2025-01-01T00:00:00Z"

},

{

"id": 5,

"tipoResposta": "Presença",

"criacao": "2025-01-01T00:00:00Z",

"atualizacao": "2025-01-01T00:00:00Z"

},

{

"id": 6,

"tipoResposta": "Ausência",

"criacao": "2025-01-01T00:00:00Z",

"atualizacao": "2025-01-01T00:00:00Z"

}

]

}

3.3 Frontend Renderiza Botões na Mensagem

💡 Ideia de Interface Proposta:


<!-- Botões padrão sempre visíveis -->

<div class="botoes-padrao">

<button class="btn-sim" onclick="selecionarResposta(1)">✅ Sim</button>

<button class="btn-nao" onclick="selecionarResposta(2)">❌ Não</button>

<button class="btn-talvez" onclick="selecionarResposta(3)">❓ Talvez</button>

<button class="btn-mais" onclick="abrirOpcoesExtras()"></button>

</div>

  

<!-- Opções extras (aparecem quando clica no +) -->

<div class="opcoes-extras" id="opcoes-extras" style="display: none;">

<button class="btn-opcao" onclick="selecionarResposta(4)">Confirmo</button>

<button class="btn-opcao" onclick="selecionarResposta(5)">Presença</button>

<button class="btn-opcao" onclick="selecionarResposta(6)">Ausência</button>

<button class="btn-opcao" onclick="selecionarResposta(7)">Interessado</button>

</div>

  

<!-- Resposta selecionada -->

<div class="resposta-selecionada">

<span id="resposta-texto">Selecionadas: </span>

<button onclick="enviarResposta()" class="btn-enviar">Enviar</button>

</div>

🔧 JavaScript da Interface:


let respostasSelecionadas = [];

const maxSelecoes = 3;

  

function selecionarResposta(tipoRespostaId) {

const resposta = obterRespostaPorId(tipoRespostaId);

if (respostasSelecionadas.includes(tipoRespostaId)) {

// Desmarcar se já estava selecionado

respostasSelecionadas = respostasSelecionadas.filter(id => id !== tipoRespostaId);

} else if (respostasSelecionadas.length < maxSelecoes) {

// Adicionar se ainda não atingiu o limite

respostasSelecionadas.push(tipoRespostaId);

}

atualizarInterface();

}

  

function atualizarInterface() {

const textoResposta = document.getElementById('resposta-texto');

const nomesRespostas = respostasSelecionadas.map(id => obterRespostaPorId(id).tipoResposta);

textoResposta.textContent = `Selecionadas: ${nomesRespostas.join(' ')}`;

// Destacar botões selecionados

document.querySelectorAll('.btn-sim, .btn-nao, .btn-talvez, .btn-opcao').forEach(btn => {

btn.classList.remove('selecionado');

});

respostasSelecionadas.forEach(id => {

const botao = document.querySelector(`[onclick="selecionarResposta(${id})"]`);

if (botao) botao.classList.add('selecionado');

});

}

  

function abrirOpcoesExtras() {

const opcoes = document.getElementById('opcoes-extras');

opcoes.style.display = opcoes.style.display === 'none' ? 'block' : 'none';

}

  

async function enviarResposta() {

if (respostasSelecionadas.length === 0) {

alert('Selecione pelo menos uma opção');

return;

}

try {

const response = await fetch(`/api/follow-up/${followUpId}/resposta-tipo`, {

method: 'POST',

headers: { 'Content-Type': 'application/json' },

body: JSON.stringify({ tipoRespostaIds: respostasSelecionadas })

});

if (response.ok) {

alert('Resposta enviada com sucesso!');

// Atualizar interface para mostrar como respondido

}

} catch (error) {

console.error('Erro ao enviar resposta:', error);

}

}

🎨 Exemplos de Uso:


🎯 FASE 4: Interface de Resposta Integrada na Mensagem

4.1 Interface Renderizada Dentro da Mensagem


<!-- Modal ou seção expandida da mensagem -->

<div class="mensagem-completa" id="mensagem-55">

<div class="mensagem-header">

<h2>📢 Confirmação de Presença - Evento Esportivo</h2>

<span class="status-badge">Aguardando Resposta</span>

</div>

<div class="mensagem-conteudo">

<p><strong>Dia:</strong> 15/01/2025</p>

<p><strong>Horário:</strong> 14h00</p>

<p><strong>Local:</strong> Ginásio Principal</p>

<p><strong>Descrição:</strong> Evento esportivo anual da instituição</p>

</div>

<div class="mensagem-respostas" id="botoes-resposta">

<!-- 💡 Interface Proposta: Botões Padrão + Opções Extras -->

<!-- Botões padrão sempre visíveis -->

<div class="botoes-padrao">

<h4>Responder:</h4>

<button class="btn-sim" onclick="selecionarResposta(1)">✅ Sim</button>

<button class="btn-nao" onclick="selecionarResposta(2)">❌ Não</button>

<button class="btn-talvez" onclick="selecionarResposta(3)">❓ Talvez</button>

<button class="btn-mais" onclick="abrirOpcoesExtras()"></button>

</div>

<!-- Opções extras (aparecem quando clica no +) -->

<div class="opcoes-extras" id="opcoes-extras" style="display: none;">

<button class="btn-opcao" onclick="selecionarResposta(4)">Confirmo</button>

<button class="btn-opcao" onclick="selecionarResposta(5)">Presença</button>

<button class="btn-opcao" onclick="selecionarResposta(6)">Ausência</button>

<button class="btn-opcao" onclick="selecionarResposta(7)">Interessado</button>

</div>

<!-- Resposta selecionada -->

<div class="resposta-selecionada">

<span id="resposta-texto">Selecionadas: </span>

<button onclick="enviarResposta()" class="btn-enviar">Enviar</button>

</div>

</div>

</div>

  

<script>

// ⭐ ID vem da notificação/mensagem

const followUpId = 36; // Obtido da notificação

let respostasSelecionadas = [];

const maxSelecoes = 3;

// Função para selecionar/deselecionar resposta

function selecionarResposta(tipoRespostaId) {

if (respostasSelecionadas.includes(tipoRespostaId)) {

// Desmarcar se já estava selecionado

respostasSelecionadas = respostasSelecionadas.filter(id => id !== tipoRespostaId);

} else if (respostasSelecionadas.length < maxSelecoes) {

// Adicionar se ainda não atingiu o limite

respostasSelecionadas.push(tipoRespostaId);

}

atualizarInterface();

}

// Função para atualizar a interface

function atualizarInterface() {

const textoResposta = document.getElementById('resposta-texto');

const nomesRespostas = respostasSelecionadas.map(id => obterRespostaPorId(id).tipoResposta);

textoResposta.textContent = `Selecionadas: ${nomesRespostas.join(' ')}`;

// Destacar botões selecionados

document.querySelectorAll('.btn-sim, .btn-nao, .btn-talvez, .btn-opcao').forEach(btn => {

btn.classList.remove('selecionado');

});

respostasSelecionadas.forEach(id => {

const botao = document.querySelector(`[onclick="selecionarResposta(${id})"]`);

if (botao) botao.classList.add('selecionado');

});

}

// Função para abrir/fechar opções extras

function abrirOpcoesExtras() {

const opcoes = document.getElementById('opcoes-extras');

opcoes.style.display = opcoes.style.display === 'none' ? 'block' : 'none';

}

// Função para enviar resposta

async function enviarResposta() {

if (respostasSelecionadas.length === 0) {

alert('Selecione pelo menos uma opção');

return;

}

try {

const response = await fetch(`/api/follow-up/${followUpId}/resposta-tipo`, {

method: 'POST',

headers: { 'Content-Type': 'application/json' },

body: JSON.stringify({ tipoRespostaIds: respostasSelecionadas })

});

if (response.ok) {

alert('Resposta enviada com sucesso!');

// Atualizar interface para mostrar como respondido

atualizarStatusResposta(respostasSelecionadas.map(id => obterRespostaPorId(id).tipoResposta).join(' '));

}

} catch (error) {

console.error('Erro ao enviar resposta:', error);

}

}

</script>


📤 FASE 5: Resposta do Usuário

5.1 Cenário A: Resposta por Índice (Resposta Rápida)

Usuário clica em “Sim” (índice 0)


async function responderPorIndice(indice, followUpId) {

try {

const response = await fetch(`/api/follow-up/${followUpId}/resposta-indice`, {

method: 'POST',

headers: { 'Content-Type': 'application/json' },

body: JSON.stringify({ indiceResposta: indice })

});

const result = await response.json();

if (response.ok) {

// Atualizar interface para mostrar resposta enviada

atualizarStatusResposta('Sim', 'respondido');

alert(`Resposta 'Sim' enviada com sucesso!`);

} else {

alert(`Erro: ${result.message}`);

}

} catch (error) {

console.error('Erro ao enviar resposta:', error);

alert('Erro ao enviar resposta');

}

}

  

// Função para atualizar a interface após resposta

function atualizarStatusResposta(resposta, status) {

const statusBadge = document.querySelector('.status-badge');

statusBadge.textContent = `Respondido: ${resposta}`;

statusBadge.className = 'status-badge responded';

// Desabilitar botões de resposta

const botoesResposta = document.querySelectorAll('.btn-resposta-rapida');

botoesResposta.forEach(botao => botao.disabled = true);

}

Request Enviado:


POST /api/follow-up/36/resposta-indice

Content-Type: application/json

  

{

"indiceResposta": 0

}

Resposta da API:


{

"message": "Resposta 'Sim' registrada com sucesso."

}

Sistema Atualiza FollowUp:


// Backend processa automaticamente:

followUp.Resposta = "Sim"; // Palavra correspondente ao índice 0

followUp.Status = StatusFollowUp.True; // Marca como respondido

followUp.DataRetorno = DateTime.Now; // Registra data da resposta

followUp.TipoRespostaId = 1; // ID da palavra "Sim"


5.2 Cenário B: Resposta por Tipo (Múltiplas Seleções)

Usuário seleciona: “Sim” + “Confirmo” + "Presença"


async function responderPorTipo() {

if (selecoes.length === 0) {

alert('Selecione pelo menos uma opção');

return;

}

if (selecoes.length > 3) {

alert('Máximo de 3 opções podem ser selecionadas');

return;

}

try {

const response = await fetch(`/api/follow-up/${followUpId}/resposta-tipo`, {

method: 'POST',

headers: { 'Content-Type': 'application/json' },

body: JSON.stringify({ tipoRespostaIds: selecoes })

});

const result = await response.json();

if (response.ok) {

alert(`Resposta '${result.message}' enviada com sucesso!`);

// Redirecionar ou fechar página

} else {

alert(`Erro: ${result.message}`);

}

} catch (error) {

console.error('Erro ao enviar resposta:', error);

alert('Erro ao enviar resposta');

}

}

Request Enviado:


POST /api/follow-up/36/resposta-tipo

Content-Type: application/json

  

{

"tipoRespostaIds": [1, 4, 5]

}

Resposta da API:


{

"message": "Resposta 'Sim Confirmo Presença' registrada com sucesso."

}

Sistema Atualiza FollowUp:


// Backend processa automaticamente:

followUp.Resposta = "Sim Confirmo Presença"; // Concatenação das 3 palavras

followUp.Status = StatusFollowUp.True; // Marca como respondido

followUp.DataRetorno = DateTime.Now; // Registra data da resposta

followUp.TipoRespostaId = null; // Não categoriza automaticamente


📊 FASE 6: Verificação das Respostas

6.1 Listar FollowUps da Mensagem


GET /api/follow-up/mensagem/55

Content-Type: application/json

6.2 Resposta da API


[

{

"id": 36,

"mensagemId": 55,

"usuarioId": "usuario-123",

"nomeDestinatario": "João Silva",

"emailDestinatario": "joao.silva@email.com",

"status": "True",

"resposta": "Sim Confirmo Presença",

"dataEnvio": "2025-01-08T10:30:00Z",

"dataRetorno": "2025-01-08T14:25:00Z"

}

]


🔄 Resumo do Fluxo Completo

| Fase | Ação | Endpoint | Resultado |

|------|------|----------|-----------|

| 1 | Criar mensagem | POST /api/mensagens | Mensagem criada + FollowUp gerado |

| 2 | Usuário recebe notificação | - | Notificação na aplicação com ID |

| 3 | Usuário clica na mensagem | - | Abre mensagem completa |

| 4 | Carregar opções | GET /api/follow-up/{id}/respostas-possiveis | Botões da tabela tiporesposta |

| 5 | Resposta por índice | POST /api/follow-up/{id}/resposta-indice | Resposta única registrada |

| 5 | Resposta por tipo | POST /api/follow-up/{id}/resposta-tipo | Até 3 palavras combinadas |

| 6 | Verificar respostas | GET /api/follow-up/mensagem/{mensagemId} | Lista todas as respostas |


🎯 Exemplos de Respostas Possíveis

Resposta por Índice:

Resposta por Tipo (Combinações):


Vantagens dos Endpoints Focados

  1. Resposta Rápida: Botões diretos para respostas comuns

  2. Flexibilidade: Combinação de até 3 palavras para respostas complexas

  3. Estruturação: Respostas organizadas e categorizadas

  4. Performance: Endpoints otimizados para cada tipo de resposta

  5. Usabilidade: Interface intuitiva para diferentes perfis de usuário


🎯 Resumo: Como o ID do FollowUp Funciona

📝 Em Linguagem Simples:

  1. Admin cria mensagem → Sistema gera um número único (ID) para cada pessoa que vai receber

  2. Usuário recebe notificação na aplicação

  3. Usuário clica na mensagem → Abre conteúdo completo com botões de resposta

  4. Frontend carrega botões da tabela tiporesposta usando o ID 36

  5. Usuário clica em “Sim” → Sistema registra resposta índice [0] = “Sim”

  6. Sistema atualiza followup → Status muda para “Respondido: Sim”

🔧 Em Linguagem Técnica:

💡 Por que isso é importante:


🎉 Este fluxo demonstra como o sistema permite respostas estruturadas e flexíveis, mantendo a simplicidade para o usuário final enquanto oferece controle total para os administradores.


Versão: 1.0
Última Atualização: Outubro de 2025
Responsável: Equipe de Desenvolvimento CordenaAi