
Conectar canais (onboarding)
Aparece sem canais conectados ou antes da 1a varredura. Cada canal é um chip clicável "Conectar"; com pelo menos 1 conectado, "Analisar agora" habilita e leva à varredura.
Funcionalidade que reúne, num só lugar, os feedbacks que os clientes deixam nos canais do negócio (avaliações, comentários e mensagens), organiza por status e ajuda a responder com sugestões da MIA.
A funcionalidade tem três fases na rota raiz /feedback, controladas por um único estado (phase):
phase: "connect"): onboarding. O usuário escolhe de quais canais quer ouvir os clientes.phase: "scanning"): a Plataforma sincroniza os feedbacks dos últimos 30 dias dos canais conectados.phase: "dashboard"): o usuário trabalha a fila de feedbacks em formato de baralho (deck) e navega para as telas satélite.A partir do painel existem as telas satélite: Todos os Feedbacks, Feedbacks Ocultos, Detalhe do Feedback e Gerenciar canais.
Dois recortes da mesma jornada: o caminho feliz (tudo sincroniza e a fila é zerada) e o caminho triste (varredura lenta, resultado parcial, falha total ou perda de conexão).
Caminho feliz
Caminho triste
FeedbackItem)| Campo | Tipo | Observação |
|---|---|---|
id | string | Identificador único |
channelId | google-business | instagram | facebook | whatsapp | Canal de origem |
themeId | tema (atendimento, entrega, produto, qualidade, etc.) | Classificação do assunto |
authorName | string | Nome do cliente |
text | string | Conteúdo do feedback |
sentiment | positive | negative | neutral | Sentimento classificado |
interactionType | avaliacao | comentario | mensagem | Tipo da interação |
rating | number | null | Nota (só faz sentido em avaliações, ex.: Google) |
relativeTime | string | Tempo relativo exibido (ver ponto 9) |
postUrl | string | Link para o post/origem |
suggestedReplies | SuggestedReply[] | Sugestões de resposta da MIA |
Cada feedback carrega duas flags independentes:
responded: respondido (true) ou não respondido (false).hidden: oculto (true) ou visível (false).As combinações são todas válidas: um feedback pode estar respondido E oculto ao mesmo tempo. As flags ficam num mapa Record<feedbackId, { responded, hidden }>. Quando as duas voltam a false, a entrada é removida do mapa (estado limpo).
| Lista | Critério |
|---|---|
| Deck / Feedbacks Recentes | !responded && !hidden |
| Todos os Feedbacks | !hidden (visíveis) |
| Feedbacks Ocultos | hidden |
ReputationScore): fora do MVPA reputação não faz parte do MVP e não é exibida em nenhuma tela. O redesign multi-tela removeu a UI de score (e o filtro "Temas da semana"). O tipoReputationScoree o cálculo (computeReputation) permanecem na camada de dados apenas para não quebrar o reducer/serviço/testes, mas o valor é ignorado pela interface. Tratar como legado/futuro, não como funcionalidade do MVP.
Quando houver UI de reputação no futuro, o cálculo atual é: total, positivos, negativos, neutros e taxa de positividade; sem feedbacks polarizados, valor neutro (50); faixas crítico (<50), atenção (50 a 69), bom (70 a 84), excelente (>=85).
Google Meu Negócio (avaliações com nota), Instagram, Facebook e WhatsApp Business. Apenas Google Meu Negócio usa nota em estrelas; os demais usam comentário ou mensagem.
relativeTime; na integração real usar um postedAt/timestamp.)As três fases da raiz (connect,scanning,dashboard) compartilham a mesma URL/feedback/. A fase vem do estado persistido, não da URL. No desktop, as telas satélite aparecem com a navegação lateral; no mobile, comportam-se como modais com header fixo cobrindo a tela.

Aparece sem canais conectados ou antes da 1a varredura. Cada canal é um chip clicável "Conectar"; com pelo menos 1 conectado, "Analisar agora" habilita e leva à varredura.

Durante a sincronização. Barra de progresso animada com mensagens rotativas, teto 92% até os dados chegarem; contador "X de Y canais".

Após a varredura concluir. Deck no estilo baralho: o card do topo é o ativo (arrastar à direita responde, à esquerda oculta, tocar abre o detalhe). Atalhos para "Todos os feedbacks" e "Gerenciar canais".

Ao tocar em um card. Ordem vertical: card do feedback, ações (Ocultar/Mostrar e Marcar como Respondido), resposta da MIA (com "Copiar resposta") e navegação Anterior/Próximo.

Sempre acessível pelo painel. Lista os visíveis (não ocultos) em ordem cronológica, com filtros (Canal, Status, Sentimento), taxa de resposta e atalho para Feedbacks Ocultos.

A partir de Todos os Feedbacks. Lista os feedbacks com hidden = true; ao abrir o detalhe, o usuário pode torná-los visíveis de novo.

A partir do painel. Reaproveita a tela de conexão em modo "manage": conectar/desconectar reflete na hora (resync ao vivo, preservando flags). Desconectar pede confirmação.

Aparece na primeira vez que houver cards no deck. Três passos (Responder, Ocultar, Gerar resposta) com botões "Próximo"/"Entendi" e "Pular tutorial".

Ao clicar em "Desconectar" um canal. Avisa que os comentários e respostas sugeridas do canal vão sair da análise até você conectar de novo.

Sync acima do limiar de espera, sem falha. Mensagem persistente, progresso preso em 92% (nunca finge 100%) e "Voltar para canais" disponível.

Falha na sincronização (nenhum canal retornou). "Não foi possível concluir a varredura" com "Tentar de novo" e "Voltar para canais".

Painel sem nenhum canal conectado. "Nenhum canal conectado" com CTA para conectar.

Canais conectados, mas zero feedbacks em 30 dias. "Nenhum feedback no período".

Todos os feedbacks já tratados. "Tudo em dia por aqui!".

Conforme o caso na tela Todos (sem canais, sem visíveis ou sem resultado de filtro). Captura: sem feedbacks visíveis.

Nenhum feedback oculto. "Nenhum feedback oculto".

Feedback removido por nova varredura/desconexão. "Feedback não encontrado" com ação de voltar.

Ao menos 1 canal ok, outros falharam após backoff. Entra no painel com alerta "Perdemos a conexão com um canal" e atalho Gerenciar canais.

Conexão perdida com um canal conectado. Alerta no topo de Recentes; os feedbacks já sincronizados continuam visíveis.

Notificações transitórias, ex.: "Feedbacks atualizados." após a atualização, e ao copiar resposta no detalhe.

Dispara ao zerar a fila de feedbacks em aberto, sobre o estado "Tudo em dia por aqui!". Efeito canvas de chuva de confetes.
X de Y canais).
Ordem vertical da tela: card do feedback, ações, resposta da MIA, navegação.
?from=): Voltar retorna à origem; Anterior/Próximo navega pelo conjunto de Todos (quando origem deck/todos) ou pelos ocultos (quando origem ocultos). A lista é congelada (snapshot) durante a navegação para permitir voltar e corrigir.postUrl) em nova aba. Para WhatsApp, leva o usuário para o chat dentro da plataforma já com a resposta gerada pela MIA no input, restando clicar em enviar.
hidden = true.
Catálogo dos estados de vazio e erro, com a condição que os dispara e a mensagem exibida. As capturas reaproveitam as imagens da galeria 4.2.

Mensagem: "Nenhum canal conectado" + CTA conectar.

Mensagem: "Nenhum feedback no período".

Mensagem: "Tudo em dia por aqui!".

Mensagens: "Nenhum feedback ainda", "Nenhum canal conectado" + CTA, ou "Nenhum feedback para os filtros" + limpar.

Mensagem: "Nenhum feedback oculto".

Mensagem: "Feedback não encontrado" + voltar.

Mensagem: "Ainda estamos processando. Alguns canais demoram um pouco mais." (teto 92%).

Painel + alerta não bloqueante "Não foi possível sincronizar o canal X" + reconectar.

Mensagem: "Não foi possível concluir a varredura" + tentar / voltar.
localStorage na chave biud:feedback-session:v1 (fase, canais, feedbacks, score, último scan, flags).biud:feedback-deck-tutorial:v1 ("seen").navigator.clipboard quando disponível (HTTPS ou localhost) e cai num fallback com textarea + execCommand quando o contexto não é seguro.Overlay com 3 passos, exibido na primeira vez que houver cards no deck:
Botões "Próximo" / "Entendi" e "Pular tutorial". Respeita prefers-reduced-motion.
services/) já abstrai a origem: cada integração real vira um adapter que produz FeedbackItem[] sem mexer na UI.Status possíveis: Tratado Intencional Conhecido (não tratado) Fora de escopo
| # | Edge case | Como visitar | Tratamento / status |
|---|---|---|---|
| 1 | Ação perdida durante a animação de swipe/carimbo | Deslizar um card e tocar imediatamente na navegação antes da animação terminar | Conhecido Os timers são limpos no unmount; o card volta como não tratado. Mitigar comprometendo a flag antes da animação. |
| 2 | Dessincronização entre abas (last-write-wins) | Abrir /feedback/ em duas abas, agir numa e observar a outra | Conhecido Não há listener de storage; a última escrita sobrescreve. Resolver ao escopar a sessão no backend. |
| 3 | Sessão não escopada por empresa | Trocar de empresa no dropdown do topo | Conhecido A sessão é global no mock. No produto, escopar por company_id. |
| 4 | Não existia atualização após o onboarding | Painel: usar o botão de atualizar no topo de Recentes | Tratado Botão de atualizar inline que re-sincroniza preservando flags. |
| 5 | Re-sincronizar apagava as flags | Marcar respondido/oculto e atualizar | Tratado A atualização preserva as flags; só a 1a varredura do onboarding zera. |
| 6 | Desconectar e reconectar zera o estado daquele canal | Gerenciar canais: desconectar e reconectar | Intencional Desconectar exclui feedbacks e flags do canal; reconectar traz tudo novo. |
| 7 | Celebração (confetti) inconsistente conforme o caminho | Tratar o último feedback pelo Detalhe e voltar ao painel | Tratado A celebração dispara sempre que a fila em aberto chega a zero por ação do usuário. |
| 8 | Contador de trial estático e sem gate | Observar "15 dias" no topo (mobile) | Fora de escopo Valor fixo; expiração não definida nessa funcionalidade. |
| 9 | relativeTime é texto fixo e não envelhece | Deixar a sessão por dias e reabrir | Conhecido O mock guarda string, não timestamp. Adicionar postedAt para tempo real. |
| 10 | Feedbacks persistidos podem ficar velhos | Carregar uma sessão antiga após mudança no shape | Conhecido Só a migração de flags legadas é defensiva; versionar o payload. |
| 11 | Recarregar no meio da varredura volta para "conectar" | Iniciar varredura e recarregar a página | Intencional Sem feedbacks ainda, a hidratação retorna ao estado de conexão. |
| 12 | Erro de varredura, retry e progresso em 92% | Erro total ?scanError=1; lenta ?scanSlow=1; backoff que recupera ?scanFlaky=instagram; parcial ?scanPartial=instagram | Tratado (mock) Contador "X de Y canais", "ainda processando", backoff/auto-retry e resultado parcial. Tela de erro cheia só quando nenhum canal retorna. Pendente: telemetria. |
| 13 | Perda de conexão com um canal conectado | ?channelError=instagram (canal precisa estar conectado) | Tratado Alerta na Recentes + estado de erro e "Reconectar" no Gerenciar canais. Feedbacks já sincronizados continuam visíveis. |
Janela de sincronização (30 dias) é hipótese inicial. O número de 30 dias foi definido como ponto de partida, sem base empírica de uso real. Nas primeiras semanas após o lançamento, acompanhar de perto o volume de feedbacks devolvidos por varredura e a demanda dos usuários por histórico mais longo ou mais curto (via suporte, entrevistas ou telemetria da seção 12). Se o volume estourar a leitura confortável do deck, considerar reduzir para 14 ou 7 dias; se os usuários pedirem contexto mais antigo, considerar ampliar para 60 ou 90 dias. Ajuste deve ser dirigido por dado, não por intuição.
Tratamento de spam ainda não implementado. O MVP entrega no painel todo comentário que o canal devolver, sem filtragem prévia. Nas primeiras semanas pós-lançamento, avaliar se aparecem no deck comentários que atrapalham a leitura — emojis soltos, mensagens repetidas, promoções de terceiros, links suspeitos, respostas automatizadas ou conteúdo claramente automatizado. Se o volume desses casos for relevante, será necessário projetar uma camada de filtragem antes da apresentação (heurísticas por comprimento mínimo, blacklist de padrões, classificação pela MIA ou combinação). Escopo, gatilho e critério de sucesso dessa camada dependem do que os dados mostrarem — não modelar preventivamente.
/feedback/?scanError=1 (com canal conectado e fase de varredura)./feedback/?scanSlow=1 para atrasar a sincronização além do limiar (~8s)./feedback/?scanFlaky=<id> (ex.: instagram). O canal falha nas primeiras tentativas e retorna numa seguinte; conclui em 100% sem marcar erro./feedback/?scanPartial=<id>. O(s) canal(is) falham permanentemente; os demais retornam e o usuário entra no painel com alerta de canal em erro./feedback/?channelError=<id> com o canal já conectado.localStorage.removeItem("biud:feedback-deck-tutorial:v1") e recarregar.localStorage.removeItem("biud:feedback-session:v1") e recarregar.| Item | Sandbox (hoje) | Produto (esperado) |
|---|---|---|
| Conexão de canal | Toggle simulado | OAuth em aba separada |
| Origem dos feedbacks | Dataset mockado | Adapters por canal (services/) |
| Classificação de sentimento e tema | Pré-definidos no seed | Serviço de NLP/LLM |
| Sugestões de resposta (MIA) | Pré-definidas no seed | Geração por modelo |
| Tempo relativo | String fixa | Derivado de postedAt |
| Escopo por empresa | Global | Por empresa (company_id) |
| Persistência | localStorage | Backend |
| Varredura lenta / tolerância | Contador "X de Y canais", "ainda processando" (~8s, ?scanSlow), backoff/auto-retry (?scanFlaky) e resultado parcial (?scanPartial); resta a telemetria | Backoff/auto-retry e resultado parcial reais por canal |
| Métricas/telemetria | Não instrumentado | Ver seção 12 |
Hoje a feature não está instrumentada; o que segue é a especificação do que deve ser enviado quando a telemetria for ligada. Toda métrica deve ser cortável pelas dimensões de negócio (porte, segmento, localização).
| Dimensão | Exemplos / valores | Origem |
|---|---|---|
company_id | identificador da empresa | sessão/empresa atual |
porte | MEI, ME, EPP, demais | cadastro da empresa |
segmento | CNAE / setor (ex.: alimentação, varejo, serviços) | cadastro |
localizacao | UF, município, região | cadastro |
plano / trial | em teste, pago, expirado, free; dias restantes de trial | billing |
canais_conectados | lista e contagem de canais | estado da feature |
dispositivo | mobile, desktop | client |
| Evento | Quando dispara | Propriedades | Métrica |
|---|---|---|---|
feedback_channel_connect_started | Clique em conectar canal | channel | Início de conexão por canal |
feedback_channel_connect_succeeded | OAuth concluído com sucesso | channel, duration_ms | Taxa de sucesso de conexão |
feedback_channel_connect_failed | Falha/cancelamento do OAuth | channel, reason | Atrito de conexão |
feedback_channel_disconnected | Confirmação de desconexão | channel, feedbacks_removidos | Churn de canal |
feedback_scan_started | Início da varredura | channels, channels_count | Funil de ativação |
feedback_scan_completed | Varredura concluída | duration_ms, feedbacks_count, por_canal, reputation_value | Volume e reputação inicial |
feedback_scan_failed | Erro na varredura | reason | Confiabilidade |
feedback_scan_retried | Clique em "Tentar de novo" | - | Recuperação de erro |
feedback_scan_slow | Varredura cruza o limiar de espera | elapsed_ms, channels_pending | Latência de sincronização |
feedback_scan_channel_retried | Auto-retry de um canal com backoff | channel, attempt | Resiliência |
feedback_scan_partial | Entra no painel com resultado parcial | channels_ok, channels_failed | Tolerância a falhas |
feedback_refresh_triggered | Botão atualizar no painel | novos_feedbacks, total | Recorrência de uso |
feedback_detail_opened | Abrir o detalhe | feedback_id, channel, sentiment, origem | Profundidade de uso |
feedback_reply_generated | MIA revela sugestão | feedback_id, tone | Uso da MIA |
feedback_reply_copied | Clique em "Copiar resposta" | feedback_id, channel, tone | Intenção de responder |
feedback_go_to_feedback_clicked | Clique em "Ir para o feedback" | feedback_id, channel, is_placeholder | Conversão para o canal |
feedback_marked_responded | Marcar respondido | feedback_id, channel, sentiment, interaction_type, via | Taxa de resposta |
feedback_unmarked_responded | Desmarcar respondido | feedback_id | Correções |
feedback_hidden | Ocultar | feedback_id, channel, sentiment | Taxa de ocultação |
feedback_unhidden | Tornar visível | feedback_id | Reversões |
feedback_deck_cleared | Fila em aberto chega a zero | total_tratados, tempo_sessao_ms | Conclusão da fila |
feedback_filter_applied | Aplicar filtro em Todos | canal, status, sentimento | Necessidade de filtragem |
feedback_tutorial_shown | Tutorial exibido | - | Alcance do onboarding |
feedback_tutorial_step | Avançar passo do tutorial | step | Engajamento no tutorial |
feedback_tutorial_completed | Concluir tutorial | - | Conclusão do tutorial |
feedback_tutorial_skipped | Pular tutorial | step | Abandono do tutorial |
feedback_scan_completed deve quebrar a contagem por canal.themeId) são dimensões ricas para cruzar com porte e segmento.postedAt (timestamp) para ordenação cronológica real e tempo relativo vivo.feedback_scan_slow, feedback_scan_channel_retried, feedback_scan_partial), que entra junto com a instrumentação geral (seção 12).Estimativa de esforço para trocar o mock por integrações reais. Baseado em conhecimento público até início de 2026. Políticas e endpoints mudam com frequência: sempre confirme na documentação oficial antes de planejar a engenharia.
| Canal | Lê via API? | Responde via API? | Auth | Gate de aprovação | Histórico retroativo | Complexidade | Prioridade |
|---|---|---|---|---|---|---|---|
| Google Play | Sim | Sim | Service account | Baixo | ~7 dias na API; histórico via relatórios | Média-baixa | Baixa |
| App Store | Sim | Sim | JWT key | Baixo | Amplo | Média-baixa | Baixa |
| Google Meu Negócio | Sim | Sim | OAuth 2.0 | Médio | Sim | Média | Alta |
| Mercado Livre | Parcial | Parcial | OAuth 2.0 | Baixo | Parcial | Média | Baixa |
| iFood | Sim | Sim | OAuth client credentials | Médio-alto | Sim (recentes) | Média-alta | Média |
| Facebook (Página) | Sim | Sim | OAuth Meta | Alto | Sim | Alta | Alta |
| Sim | Sim | OAuth Meta | Alto | Limitado | Alta | Alta | |
| WhatsApp Business | Só fluxo novo | Sim (janela 24h) | Meta WABA | Alto | Não | Alta | Alta |
| Reclame Aqui | Contratual | Contratual | Parceria/B2B | Comercial | Depende | Muito alta | Baixa |
reviews.list só traz avaliações recentes (~7 dias); histórico completo via export do Play Console.customerReviews + customerReviewResponses), JWT local, histórico amplo, sem App Review.business.manage; gate principal é a aprovação/quota do Google.A classificação de sentimento e a geração de respostas (hoje pré-definidas no seed) são os próximos blocos a trocar por um serviço real (NLP/LLM).
Recomendações de como a MIA deve julgar cada feedback como positivo, negativo ou neutro. No mock o sentimento já vem no seed; quando entrar um modelo real (NLP/LLM), usar estas diretrizes como base do prompt/treinamento. O sentimento define a cor do card e a separação "elogios vs oportunidades de melhoria".
Erros de classificação têm custo assimétrico: marcar um negativo como positivo faz uma reclamação parecer elogio e atrasa a resposta ao cliente insatisfeito. Na dúvida entre positivo e negativo, prefira sinalizar como negativo/atenção para não perder uma oportunidade de recuperação. O tema (themeId) é classificação complementar e não altera o sentimento.
Proposta de como avisar o lojista sobre novos feedbacks fora do app. Ainda não implementado (o MVP é só a tela). O princípio para o público (MPEs/SMBs) é avisar o que exige ação, sem virar ruído: o negativo chega na hora; o resto vira resumo.
| Evento | Cadência | Canais sugeridos (default) |
|---|---|---|
| Feedback negativo novo | Instantâneo | Push (on); WhatsApp (opt-in); e-mail no digest |
| Feedback positivo novo | Agrupado (não instantâneo) | Entra no digest; sem push individual |
| Avaliação com nota baixa (1 a 2) no Google | Instantâneo | Push; WhatsApp (opt-in); e-mail no digest |
| Resumo periódico (digest) | Semanal | |
| Fila acumulando (ex.: 10+ em aberto) | No máximo 1x/dia | Push ("você tem N feedbacks para responder") |
| Canal com conexão perdida (regra 15) | Instantâneo (1x, sem repetir) | Push + e-mail |
Por que assim: o negativo tem custo de não responder rápido, então vai instantâneo. O positivo não precisa interromper, então é agrupado. O digest semanal dá a visão geral sem pressão diária.
Métricas para acompanhar (ligam à seção 12): notificações enviadas/abertas por canal e tipo, tempo até a primeira resposta após a notificação, opt-out por tipo, e impacto na taxa de resposta. Cortar por porte, segmento e localização.