teqo-backup/docs/plans/pixel-meta-abaixo-assinado.md
Francisco Ignácio Fontoura Solla be1106f887 Add Meta Pixel tracking to public petition pages.
Let marketing configure a numeric facebookPixelId per petition in Payload, fire PageView on load and Lead after a successful signature, and mark the roadmap item delivered with deferred follow-ups documented.
2026-07-21 10:51:47 -03:00

146 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Pixel do Meta (Facebook) nos abaixo-assinados
Status: entregue 2026-07-21
Atualizado em: 2026-07-21
Item do roadmap: [docs/roadmap.md](../roadmap.md) (Site público — entregue)
Impeccable: B — encaixe no admin `Petition` + script na página `/abaixo-assinado/[id]` e no sucesso do `PetitionForm`
Appetite: ~0,5–1 dia eng; migration + 1 campo admin + componente de script + disparo `Lead` no sucesso
Responsável: —
_Revisão 2026-07-21: implementação + `/simplify` + `capture-review-debts` — status entregue; unicidade travada; gatilho S10 (fila/retry de `Lead`) em Adiado; débitos de triage registrados em Explicitamente fora._
## Design (Impeccable)
Âncoras: `PRODUCT.md` / `DESIGN.md` (register `brand` no site público; tema `data-theme='petition'` na página). Admin Payload usa labels pt-BR do grupo `Abaixo-assinados` — sem redesign de paleta.
Na implementação (`implement-roadmap-item`): craft compacto → critique → polish (superfície mínima; sem shape longo).
Brief compacto:
- **Persona / contexto:** marketing digital monta campanha de ads no Meta e precisa ver assinaturas como conversões; editor no `/admin` cola o ID do Pixel no abaixo-assinado.
- **Job principal:** atribuir assinaturas reais a um Pixel por petição, sem o editor colar JavaScript arbitrário.
- **Estratégia de cor:** Restrained (tokens `petition` / admin Payload existentes).
- **Anti-goals:** textarea de snippet HTML; GTM/multi-vendor; cookie banner completo; CAPI/Advanced Matching nesta fatia.
## Contexto
A página pública `/abaixo-assinado/[id]` renderiza o documento `petition` e o formulário cliente `PetitionForm`. Assinatura bem-sucedida chama `submitPetitionSignature` e abre `PetitionSuccessDialog`. Com Pixel configurado: `MetaPixel` dispara `PageView` no load; `trackMetaLead` dispara `Lead` após assinatura ok.
A collection `Petition` (`src/collections/Petition.ts`) expõe o grupo `tracking.facebookPixelId` (admin “Rastreamento / Ads”) além do grupo `form`.
O time de marketing digital pediu (2026-07-21) um campo no formulário do abaixo-assinado no Payload para “adicionar o snippet do Pixel do Facebook” e acompanhar assinaturas no Events Manager / otimizar campanhas.
### O que o Meta Pixel exige (pesquisa)
Fontes: [Meta Pixel Get Started](https://developers.facebook.com/docs/meta-pixel/get-started) (atualizado 2026-06-30), [Conversion tracking](https://developers.facebook.com/docs/meta-pixel/implementation/conversion-tracking), [Deduplicate Pixel + CAPI](https://developers.facebook.com/docs/marketing-api/conversions-api/deduplicate-pixel-and-server-events/).
1. **Base code** no `<head>` (ou via `next/script`): carrega `fbevents.js`, chama `fbq('init', PIXEL_ID)` e `fbq('track', 'PageView')`. O único dado que o editor precisa fornecer é o **Pixel ID** (número do Dataset no Events Manager) — o restante do “snippet” é boilerplate fixo.
2. **Evento de conversão para lead-gen:** `fbq('track', 'Lead', {…})` **somente após** a assinatura bem-sucedida — nunca no carregamento da página do formulário (erro clássico que infla leads 5–10× e corrompe o otimizador).
3. **Parâmetros úteis no `Lead`:** `content_name` (título/id da petição). `eventID` opcional agora; obrigatório se/quando houver Conversions API (CAPI) para deduplicação.
4. **CAPI + Advanced Matching** (e-mail/telefone hasheados no servidor) melhoram Event Match Quality (~6.0+), mas exigem secret de access token, hashing LGPD-consciente e dedup — fora do appetite desta fatia.
5. **LGPD:** o Pixel define cookies Meta (`_fbp` etc.) e envia sinais de navegação/conversão. Transparência em `/privacidade` (e, se o jurídico pedir, no texto de Consent da petição) é requisito de produto; cookie banner site-wide fica adiado.
## Objetivos
- Campo opcional no admin de `petition` para o marketing configurar o Pixel **por abaixo-assinado**.
- Na página pública: carregar base code + `PageView` quando o ID estiver presente.
- No sucesso da assinatura: disparar exatamente um `Lead` (não no page load).
- Guardrails: **nunca** persistir nem executar HTML/JS colado pelo editor; validar ID numérico; sem env var obrigatória (config por documento); migration com `push: false`; cache já revalidado por `revalidateDocumentById` no `afterChange` existente.
- Sem novo `Consent` key nesta fatia (petição já exige Consent de assinatura); sem CAPI.
## Decisões travadas
- **Persistir só `facebookPixelId` (texto, dígitos), não o snippet HTML.** O “snippet” que o Meta mostra no Events Manager é boilerplate + ID; colar HTML no admin é XSS / injection e quebra CSP. O app injeta o base code controlado em código. **Rejeitado:** `textarea`/`code` com snippet livre; campo `html`/`richText` de tracking; GTM container ID como substituto nesta fatia.
- **Pixel por petição (campo no documento `petition`), não global do site.** Campanhas de ads costumam ser por abaixo-assinado/URL; evita Pixel único vazar PageViews de notícias/home. **Rejeitado:** só env `NEXT_PUBLIC_FACEBOOK_PIXEL_ID` site-wide; Global `SiteSettings` único (pode ser fill-in futuro se marketing unificar Dataset).
- **v1 = browser Pixel only, com os dois eventos:** `PageView` no load da página da petição **e** `Lead` no sucesso do form (confirmado com produto 2026-07-21). Padrão Meta: retargeting de visitantes + conversão de assinatura. Transparência de cookies/Meta fica no polish de `/privacidade` (dependência suave). **Rejeitado nesta fatia:** só `Lead` sem `PageView`; Conversions API; Advanced Matching com e-mail/telefone no browser/servidor; eventos `CompleteRegistration`/`Contact` além de `Lead`; cookie banner / CMP.
- **`Lead` só após `submitPetitionSignature` ok** (mesmo caminho que abre o diálogo de sucesso). **Rejeitado:** `Lead` no `onSubmit` antes da resposta; `Lead` no page load; thank-you page dedicada (não existe hoje — o sucesso é modal same-page).
- **Mesmo Dataset em N petições permitido.** O mesmo `facebookPixelId` pode aparecer em várias petições; `content_name` no `Lead` carrega o título da petição para diferenciar no Events Manager. **Rejeitado:** uniqueness forçada no admin.
- **i18n e naming** seguem o AGENTS.md: identificadores em inglês (`facebookPixelId`, `MetaPixel`, `trackMetaLead`); labels admin e copy em pt-BR (“ID do Pixel do Meta (Facebook)”).
## Abordagem proposta
```mermaid
flowchart LR
Admin["Payload admin<br/>tracking.facebookPixelId"]
Page["abaixo-assinado/[id]<br/>Server Component"]
Pixel["MetaPixel client<br/>next/script + fbq init/PageView"]
Form["PetitionForm"]
Action["submitPetitionSignature"]
Lead["trackMetaLead<br/>fbq track Lead"]
Meta["Meta Events Manager"]
Admin --> Page
Page --> Pixel
Pixel --> Meta
Form --> Action
Action -->|sucesso--> Lead
Lead --> Meta
```
Componentes:
- **Campo `facebookPixelId`** em `src/collections/Petition.ts` (irmão do grupo `form` ou dentro de um group `tracking` com label “Rastreamento / Ads”): `type: 'text'`, opcional, `admin.description` explicando “cole só o ID numérico do Events Manager (ex.: 123456789012345), não o snippet HTML completo”. `validate` rejeita não-dígitos / comprimento fora de ~5–20. Sem `required`.
- **`isValidFacebookPixelId` / `normalizeFacebookPixelId`** (em `src/lib/facebookPixel.ts` ou `src/utilities/facebookPixel.ts`): helper puro testável (unit) — depth check: um módulo pequeno que esconde a regex e a montagem do init; não inventar “AnalyticsService”.
- **`MetaPixel`** (client, `src/components/MetaPixel.tsx`): se `pixelId` válido, renderiza `next/script` (`strategy="afterInteractive"`) que carrega o padrão Meta (`fbevents.js` + `fbq('init', id)` + `fbq('track', 'PageView')`) e noscript img opcional. Tipar `window.fbq` localmente (sem `@types` de terceiros se não existir no repo).
- **`trackMetaLead`** (mesmo módulo lib/utilities ou export do componente): `fbq('track', 'Lead', { content_name }, { eventID? })` se `window.fbq` existir; no-op seguro se Pixel não carregou / adblock.
- **`PetitionForm`**: após sucesso de `submitPetitionSignature`, chamar `trackMetaLead` com `content_name` = título ou id da petição (e `eventID` = UUID gerado no cliente **só se** quisermos preparar CAPI depois — nesta fatia opcional; preferir gerar UUID e passar já, custo zero, facilita fatia futura).
- **Página** `abaixo-assinado/[id]/page.tsx`: passar `petition.facebookPixelId` para `<MetaPixel />` quando presente; incluir o campo no `select`/cache se a leitura de documento filtrar campos (hoje `getCachedDocumentById` carrega o doc — confirmar após `generate:types`).
- **Migration** `20260721_133531_add_petition_facebook_pixel_id` — colunas `tracking_facebook_pixel_id` / `version_tracking_facebook_pixel_id`; sem backfill.
- **Testes:** `tests/unit/facebookPixel.unit.spec.ts` (validador + `trackMetaLead`); smoke manual com [Meta Pixel Helper](https://developers.facebook.com/docs/meta-pixel/support/pixel-helper).
## Já resolvido no simplify/critique (não reabrir)
- Normalização única na página (`normalizeFacebookPixelId`) + prop `facebookPixelId` para `PetitionForm` (sem re-normalizar no form).
- `validate` admin e `beforeChange` em `Petition` reutilizam `normalizeFacebookPixelId`.
- Validação duplicada removida de `MetaPixel` (prop pré-validada).
- Tipos one-off removidos (`TrackMetaLeadInput`, `MetaPixelProps`); `trackMetaLead` com assinatura posicional.
- Blockquote da página da petição suavizado (Impeccable side-tab); `impeccable detect` limpo na superfície.
## Dependências
- Nenhuma de outro plano de trilha A/B/C/D.
- **Suave:** polish do Aviso de Privacidade (Site público / Onda 0 textos finais) deve mencionar cookies/ferramentas de ads (Meta) quando Pixel estiver em uso — não bloqueia a engenharia do campo.
- Reusa: `revalidateDocumentById` já no `afterChange` de `Petition`; `PetitionForm` success path; `next/script`.
## Não escopo
- Conversions API (CAPI) / access token / Event Match Quality server-side → fatia futura com gatilho abaixo.
- Google Tag Manager, TikTok Pixel, Google Ads gtag — outro item se marketing pedir.
- Cookie consent banner site-wide / CMP.
- Pixel no layout global do site (home, notícias, `/campanha`).
- Thank-you page dedicada / redirect pós-assinatura.
- Alterar schema de `signature`/`contact` para guardar `event_id` Meta.
## Rabbit holes
- **Colar snippet HTML “como o Meta manda”.** Se alguém “só completar” o pedido literal do marketing: XSS + HTML no CMS. **Mitigação:** campo só ID + description no admin + validação server-side.
- **CAPI “já que estamos no servidor”.** Explode em secrets, hashing PII, dedup, retries. **Mitigação:** defer com gatilho.
- **Cookie banner completo por causa deste campo.** Projeto de CMP. **Mitigação:** transparência em `/privacidade` + decisão jurídica sobre PageView; não construir banner aqui.
- **Advanced Matching no browser com e-mail/telefone em claro.** Vazamento + LGPD. **Mitigação:** fora; CAPI hasheado só em fatia futura.
## Adiado com gatilho
- **Fila/retry de `Lead` quando `window.fbq` ainda não carregou** (race submit rápido vs. `afterInteractive`). Revisitar quando smoke com [Meta Pixel Helper](https://developers.facebook.com/docs/meta-pixel/support/pixel-helper) mostrar conversão `Lead` ausente após assinatura bem-sucedida em produção.
- **Conversions API + dedup com `event_id` compartilhado.** Revisitar quando: marketing reportar perda material de atribuição (iOS/adblock) **ou** Event Match Quality &lt; 6 no Dataset em uso com volume real de ads.
- **Pixel / Dataset global em `SiteSettings`.** Revisitar quando: o mesmo ID for colado em ≥3 petições e o admin reclamar de repetição.
- **Cookie banner / CMP.** Revisitar quando: jurídico exigir bloqueio pré-consent de cookies de ads (não só transparência).
## Explicitamente fora (triage capture-review-debts 2026-07-21)
- **Re-validação em `trackMetaLead`** — defesa em profundidade intencional (custo zero).
- **Flatten `tracking` → campo top-level** — grupo reservado para futuros campos de ads.
- **`eslint` `@next/next/no-img-element` no `<noscript>` 1×1** — padrão Meta; `next/image` inadequado.
- **Int test de `Petition.validate`** — opcional no appetite original; sem evidência de regressão.
- **Mencionar cookies/Meta em `/privacidade`** — absorvido no item existente do roadmap (Site público / O0+); não duplicar plano.
## Referências
- `docs/roadmap.md` (Site público)
- `src/collections/Petition.ts` — schema admin do abaixo-assinado
- `src/app/(frontend)/abaixo-assinado/[id]/page.tsx` — página pública
- `src/components/PetitionForm.tsx` — sucesso da assinatura
- `src/app/(frontend)/actions/submitPetitionSignature.ts` — escrita transacional (inalterada no fluxo de tracking cliente)
- Meta: [Get Started](https://developers.facebook.com/docs/meta-pixel/get-started) · [Conversion tracking](https://developers.facebook.com/docs/meta-pixel/implementation/conversion-tracking) · [Deduplication](https://developers.facebook.com/docs/marketing-api/conversions-api/deduplicate-pixel-and-server-events/)
- Next.js: `next/script` + `strategy="afterInteractive"` (App Router)
- AGENTS.md — naming, migrations `push: false`, Consent por chave (não inventar paralelo aqui), revalidate de documentos
- `PRODUCT.md` / `DESIGN.md` — canal público / tema `petition`