@joserqc/feedback-widget
v0.10.0
Published
Widget de feedback in-app da plataforma ZexIA — botão flutuante com captura de tela e contexto técnico
Maintainers
Readme
@joserqc/feedback-widget
Widget de feedback in-app da plataforma ZexIA: botão flutuante → modal de bug/sugestão com captura de tela opcional, logs de console e contexto técnico → painel central de triagem.
Instalação
pnpm add @joserqc/feedback-widgetReact ^18.2.0 || ^19.0.0 (peer). As libs de captura (@zumer/snapdom,
modern-screenshot) carregam por import lazy — fora do bundle inicial.
Uso
import { FeedbackWidget } from '@joserqc/feedback-widget';
<FeedbackWidget
projectKey="fbk_..."
user={{ id: usuario.id, email: usuario.email, name: usuario.nome, role: usuario.papel }}
metadata={{ tenantId }}
appVersion="1.4.2"
position="bottom-right"
offset={{ bottom: 96, side: 24 }}
accentColor="#0f766e"
/>Monte uma única vez, dentro da área autenticada. Client-only: no Next.js
(App Router), envolva em um componente com 'use client'.
Props
| Prop | Tipo | Descrição |
| --- | --- | --- |
| projectKey | string (obrigatória) | Chave pública do projeto (fbk_...) |
| apiUrl | string | Base da API (default https://feedback.zexia.tech) |
| user | { id?, email?, name?, role? } | Usuário logado (recomendado) |
| metadata | object | Contexto extra do app (≤16KB) |
| appVersion | string | Versão do app host |
| position | 'bottom-right' \| 'bottom-left' | Posição do botão |
| offset | number \| { bottom?, side? } | Distância das bordas em px (default 20); objeto separa vertical/lateral para não colidir com outros FABs |
| accentColor | string | Cor de destaque do modal/botão |
| zIndex | number | Default 2147482000 |
| enabled | boolean | Desliga o widget sem desmontar (default true) |
| captureScope | 'page' \| 'viewport' | O que entra na captura (default page): a página inteira rolável ou só a área visível da janela. O aviso mostrado ao usuário acompanha a escolha |
| keepHostOverlaysOpen | boolean | Impede que a interação com o widget feche o modal/popover aberto do app (default true) — é o que permite reportar um problema dentro de um modal com ele visível na captura. Desligue só se o host precisar enxergar cliques globais |
Requisitos no app host
- Origem permitida: o ingest valida o header
Origincontra as origens do projeto — cadastre o domínio do app (ehttp://localhost:<porta>em dev) no painel, senão o envio retorna 403. - CSP (se o app tiver):
connect-src https://feedback.zexia.techestyle-src 'unsafe-inline'(o CSS do widget é auto-injetado). Sem isso o widget falha silenciosamente. - Escopo da captura:
captureScope="viewport"recorta a imagem para o que está visível na janela — menos dado exposto e arquivo menor. Não deixa a captura mais rápida: as duas libs serializam o DOM inteiro de qualquer forma, e é o recorte que muda, não o trabalho. Para reduzir o tempo em página pesada, marque a parte cara comdata-fbw-mask. Se o recorte falhar, o widget devolve captura nenhuma em vez da página inteira — o aviso exibido ao usuário promete só a área visível e mandar mais do que isso quebraria o consentimento. - LGPD: adicione
data-fbw-maska elementos com dados sensíveis — eles saem da captura de tela. O usuário sempre vê um preview da captura antes de enviar. Não logue PII emconsole.log(os últimos logs vão junto do feedback, com redação best-effort de tokens/senhas).
Anotação na captura (>=0.3)
Sobre o preview, o usuário pode marcar onde está o problema com círculo, seta ou caneta (com desfazer/limpar). As marcas são compostas na própria imagem antes do envio — nada muda no contrato da API. Falha na composição nunca bloqueia o envio (a captura segue sem marcas).
A partir da 0.4, com captura presente o modal expande para o centro da tela (até 960px; prévia com até 62vh) — no desktop o formulário fica à esquerda e a imagem grande à direita, facilitando localizar o elemento a destacar. Sem captura (ou após o envio), o modal volta ao formato compacto junto ao botão.
Em telas estreitas ou baixas, a prévia tem altura limitada, o conteúdo central rola e as ações permanecem em um rodapé aderente. O modo inicial da prévia é “Rolar”; círculo, seta e caneta só passam a capturar gestos depois que o usuário escolhe explicitamente uma ferramenta.
Imagens extras (>=0.9)
Além da captura automática, o usuário pode selecionar, arrastar ou colar até três prints extras. O modal mostra a prévia de cada arquivo e permite removê-lo antes do envio. São aceitos PNG, JPEG e WebP, com até 5MB por imagem e 8MB no total somando a captura automática.
O painel técnico, o portal do cliente e get_feedback no MCP recebem a mesma
galeria por URLs assinadas do bucket privado. Falha de uma imagem não descarta
o relato textual; o retry usa identificadores estáveis para completar uploads
sem criar outro feedback.
Carregamento da captura (>=0.6)
O modal abre já no formato final, com spinner e área reservada enquanto a captura é gerada — sem salto de layout, e interagir durante o carregamento (trocar o tipo, digitar) nunca fecha o modal. Fechar pelo fundo escurecido exige pressionar e soltar fora do modal. Captura que falha oferece "Tentar novamente"; páginas acima do limite de canvas do navegador (~32k px de altura) caem nesse estado em vez de enviar imagem vazia.
Atalhos de teclado (>=0.4)
Esc fecha o modal. Ctrl+Enter (Cmd+Enter no Mac) envia o feedback de
qualquer campo do modal — inclusive para tentar novamente após erro. Enter
sozinho continua inserindo nova linha na mensagem.
Comportamento de rede
Timeout de 15s + 1 retry automático com backoff; reenvio não duplica
(idempotência por client_event_id). Se falhar de vez, o texto digitado é
preservado com botão "Tentar novamente". Imagens cross-origin sem CORS saem em
branco na captura — o envio nunca é bloqueado por falha de screenshot.
Desde a 0.9.2, o widget também cria um reporterToken criptográfico antes da
primeira tentativa e o preserva junto do clientEventId. Se o servidor gravar o
feedback mas o 201 se perder na rede, a API devolve o token no 200
deduplicado somente quando seu hash confere com o já persistido. Assim, o caso
continua disponível em “Meus envios” sem guardar token em claro no servidor.
APIs anteriores ignoram o novo campo e continuam compatíveis, mas não recuperam
o token nesse cenário específico até serem atualizadas.
Atualização de “Meus envios” (>=0.9.1)
O widget consulta os casos pendentes 2,5 segundos após montar e volta a sincronizá-los ao abrir o modal, ao recuperar foco e quando a página volta a ficar visível. Chamadas disparadas por foco têm intervalo mínimo de 15 segundos. Uma triagem automática concluída já aparece como “Em análise”, sem alterar o status humano armazenado no painel.
Versionamento
0.x durante a fase de integrações (o contrato do ingest evolui apenas de
forma aditiva); 1.0.0 será congelada com os apps de produção rodando.
