mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 15:13:07 +00:00
Merge pull request #523 from fsolla/plans/plan-issue-17
docs(C108,D6): planos de intenção — sino de notificações (ler ao abrir + modal desktop) e push no device em produção
This commit is contained in:
commit
8ab4c5a326
2 changed files with 145 additions and 0 deletions
66
docs/plans/push-notificacoes-dispositivo-producao.md
Normal file
66
docs/plans/push-notificacoes-dispositivo-producao.md
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
# D6 — Push no dispositivo funcionando de ponta a ponta (notificações nativas em produção)
|
||||
|
||||
Status: registrado
|
||||
Atualizado em: 2026-08-09
|
||||
Issue: #521
|
||||
Priority: P1
|
||||
Model: cursor-grok-4.5-medium
|
||||
Model-local: deepseek-v4-high
|
||||
Impeccable: A — N/A sem superfície UI nova (a tela de opt-in já existe; entrega é verificação/config)
|
||||
Canvas UI: N/A — sem UI
|
||||
Appetite: ~1 dia eng + passos humanos (envs de produção / consentimento) — verificação ponta a ponta com device real
|
||||
Responsável: —
|
||||
|
||||
## Intenção
|
||||
|
||||
D2 entregou push + sino em código, mas na prática o device do usuário não recebe notificação: só o mecanismo interno ao app (sino) funciona. Eventos da campanha devem disparar também o banner nativo do sistema (a notificação padrão do device), inclusive com o app fechado.
|
||||
|
||||
## Persona e fluxo
|
||||
|
||||
- **Persona / contexto:** coordenador/assessor que ativou (ou quer ativar) avisos push no perfil; liderança em campo.
|
||||
- **Job principal:** ser avisado no device quando algo importante acontece na campanha, sem precisar abrir o app.
|
||||
- **Fluxo desejado:** ativa "Avisos push" em `/campanha/perfil` (consentimento LGPD) → evento da campanha (novo update de município, novo apoiador, atividade que precisa de atenção, convite aceito) → banner nativo no device → toque abre a tela do evento dentro do `/campanha`.
|
||||
- **Anti-goals de produto:** não reescrever a mecânica de push existente; não criar canal novo (WhatsApp continua fora — D3–D5 `wontfix`); push continua exclusivo da vertical `/campanha` (decisão travada do D2).
|
||||
|
||||
## Objetivo e aceite
|
||||
|
||||
- Com as VAPID keys configuradas em produção e o consentimento `campanha-notificacoes-push` publicado no admin, um dispositivo real optado recebe o banner nativo para cada tipo de evento que hoje gera notificação in-app — e o toque no banner abre a tela correta do evento.
|
||||
- O diagnóstico da cadeia fecha com evidência para cada elo: envs de produção (VAPID + `NEXT_PUBLIC_VAPID_PUBLIC_KEY`), consentimento publicado no admin, inscrição push registrada por device, entrega confirmada pela plataforma de push, banner exibido no OS. Bloqueio encontrado → corrigido ou escalado com dono nomeado (env → Vercel; consentimento → assessoria jurídica via lote Onda 0).
|
||||
- Limitação documentada no produto: iOS só recebe push com o PWA instalado na tela inicial (a UI do perfil já comunica) — não é defeito a corrigir aqui.
|
||||
|
||||
## Dados (intenção)
|
||||
|
||||
Dados: N/A — verificação de entrega ponta a ponta; nenhuma métrica nova de produto.
|
||||
|
||||
## Direção no codebase (hipótese)
|
||||
|
||||
- **Áreas prováveis:** cadeia já implementada em `src/utilities/notification/` (`createCampaignNotification.ts`, `sendCampaignPush.ts` — web-push/VAPID, `notificationEvents.ts` — eventos que geram notificação); service worker em `src/utilities/campaignPwa.ts` (handlers push + notificationclick); opt-in em `src/components/campaign/auth/CampaignPushNotificationsCard.tsx` (`/campanha/perfil`); chave de consentimento em `src/lib/campaignConsentKeys.ts`.
|
||||
- **Config de produção:** Vercel envs `VAPID_PUBLIC_KEY` / `VAPID_PRIVATE_KEY` / `NEXT_PUBLIC_VAPID_PUBLIC_KEY` (documentadas no `.env.example`); consentimento criado por admin (texto já existe no lote Onda 0 em `src/lib/onda0ConsentTexts.ts`).
|
||||
- **Risco de acoplamento:** fail-closed de consentimento (nunca quebrar — sem consentimento publicado, a ativação continua bloqueada); assimetria de papéis na notificação por destinatário.
|
||||
|
||||
## Dependências
|
||||
|
||||
- D2 (entregue, imutável). Nenhuma dura nova.
|
||||
|
||||
## Fora de escopo
|
||||
|
||||
- WhatsApp Business API / bridges (D3–D5, `wontfix`).
|
||||
- Agrupamento/dedupe de notificações e expurgo (questões em aberto do D2 — item próprio se quiser).
|
||||
- Convite/nudge de ativação de push dentro do sino (ver questão em aberto).
|
||||
|
||||
## Rabbit holes de produto
|
||||
|
||||
- **Depurar código que já está correto.** Se alguém "só completar" sem checar config primeiro, perde tempo reescrevendo a cadeia. **Corte:** fechar o diagnóstico elo a elo (envs → consentimento → inscrição → entrega); só tocar código com evidência de defeito.
|
||||
- **Querer "consertar" iOS Safari sem PWA instalado.** Limitação de plataforma. **Corte:** comunicar e seguir; validar entrega no Android.
|
||||
- **Push para o site público.** Decisão travada do D2: push só em `/campanha`. **Corte:** manter.
|
||||
|
||||
## Questões em aberto (produto)
|
||||
|
||||
- **Staff que não optou merece um convite para ativar push (nudge no sino)?** **Opções:** A) não — só o card do perfil | B) convite discreto na primeira abertura do sino | C) banner amplo. **Recomendação:** B, mas como item separado — não inflar esta verificação. _(decisão do gate)_
|
||||
- **Onde validar a entrega em device real?** **Opções:** A) Android Chrome obrigatório; iOS (PWA instalado) opcional nesta entrega | B) ambos obrigatórios. **Recomendação:** A — o Android valida a cadeia inteira; o iOS exige aparelho + instalação e a limitação já é conhecida.
|
||||
|
||||
## Referências
|
||||
|
||||
- D2 — `docs/plans/notifications.md` (Issue #29, entregue; imutável)
|
||||
- `AGENTS.md` — Consent fail-closed; lote jurídico Onda 0; D3–D5 `wontfix`
|
||||
- Canvas UI (gate): N/A
|
||||
79
docs/plans/sino-notificacoes-leitura-auto-modal-desktop.md
Normal file
79
docs/plans/sino-notificacoes-leitura-auto-modal-desktop.md
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
# C108 — Sino de notificações: ler ao abrir + painel limpo + modal centrado no desktop
|
||||
|
||||
Status: registrado
|
||||
Atualizado em: 2026-08-09
|
||||
Issue: #522
|
||||
Priority: P2
|
||||
Model: composer-2.5
|
||||
Model-local: deepseek-v4-flash-high
|
||||
Impeccable: C — encaixe no painel do sino (shell do `/campanha`, mobile + desktop)
|
||||
Canvas UI: /home/fsolla/.cursor/projects/home-fsolla-cursor-worktrees-teqo-plans-plan-issue-17/canvases/plan-c108-ui-draft.canvas.tsx
|
||||
Appetite: ~0,5–1 dia eng; um encaixe no painel existente, sem superfície nova
|
||||
Responsável: —
|
||||
|
||||
## Intenção
|
||||
|
||||
O staff abre o sino de notificações e precisa de um clique extra ("Marcar todas como lidas") para algo que deveria resolver na abertura — e depois ainda de "Fechar". O painel carrega título, contador e botões que não ajudam a decisão; no desktop ele é um sheet ancorado na base, quando o esperado é um modal central que some ao clicar fora.
|
||||
|
||||
## Persona e fluxo
|
||||
|
||||
- **Persona / contexto:** coordenador/assessor/liderança em `/campanha` — mesa (desktop) ou campo (mobile), abrindo o sino para ver o que aconteceu.
|
||||
- **Job principal:** ver os avisos e voltar ao trabalho; abrir já deixa tudo lido, sem interação extra.
|
||||
- **Fluxo desejado:** toca no sino → painel abre (desktop: modal central com X no canto superior direito; mobile: sheet de base) → lista carrega e marca tudo como lido no ato (badge do sino zera) → toca num item e vai para a tela, ou fecha por X / clique fora / swipe.
|
||||
- **Anti-goals de produto:** não criar leitura individual/por item; não virar central de notificações com configurações e histórico; não mexer nos gatilhos de abertura (slot do header).
|
||||
|
||||
### Esboço de fluxo (C)
|
||||
|
||||
```text
|
||||
[Abrir o sino]
|
||||
→ desktop: modal centralizado com X no canto superior direito; fora = fecha
|
||||
→ mobile: bottom sheet de base; swipe/backdrop fecha
|
||||
→ lista carrega e marca tudo como lido no ato (badge zera, sem clique)
|
||||
→ [item?] → navega para a tela do evento e fecha o painel
|
||||
→ [fechar] → X / clique fora / swipe (sem botão "Fechar")
|
||||
```
|
||||
|
||||
## Objetivo e aceite
|
||||
|
||||
- Abrir o painel marca todas as notificações do usuário como lidas automaticamente — o badge do sino zera sem nenhuma ação (o "Marcar todas como lidas" deixa de existir como botão).
|
||||
- O painel não exibe título "Notificações" nem botão "Fechar", em nenhum tamanho de tela.
|
||||
- No desktop o painel abre como modal centralizado, com X de fechar no canto superior direito e clique-fora fechando — usando o componente shadcn já existente no repo (precedente do par responsivo Dialog/Drawer em C94).
|
||||
- No mobile continua bottom sheet; o destaque visual de "não lida" na lista deixa de fazer sentido (nada é não lido depois da abertura) e pode sair.
|
||||
- Guardrail: leader lockdown intacto — notificações continuam por destinatário (`recipient` = campaignUser); nenhuma mudança de acesso, consentimento ou schema.
|
||||
|
||||
## Dados (intenção)
|
||||
|
||||
Dados: N/A — contagem já existe (badge e `read_at`); nenhuma métrica nova.
|
||||
|
||||
## Direção no codebase (hipótese)
|
||||
|
||||
- **Áreas prováveis:** `src/components/campaign/shell/CampaignNotificationBell.tsx` (painel atual: Drawer + footer com botões); ação já existente `markAllCampaignNotificationsRead` em `src/app/(campaign)/campanha/actions/notifications.ts` — passa a ser disparada na abertura; `src/components/ui/dialog.tsx` (X de fechar já existe) + `src/components/ui/Drawer.tsx`.
|
||||
- **Precedente a olhar:** par responsivo Dialog/Drawer do `CalendarFeedDialog` (C94) e o padrão "sheet sem título" dos itens mobile recentes (C106/C107).
|
||||
- **Risco de acoplamento:** shell do `/campanha` (headers desktop/mobile) — o gatilho do sino é slot existente; não trocar o contrato.
|
||||
|
||||
## Dependências
|
||||
|
||||
- Nenhuma dura. C106/C107 (superfícies diferentes) são independentes.
|
||||
|
||||
## Fora de escopo
|
||||
|
||||
- Leitura individual/por item ou "deixar X como não lida" — decisão de não fazer (anti-goal).
|
||||
- Push para o device: D6.
|
||||
- Central de notificações (histórico, expurgo, preferências) — não existe hoje; não inventar aqui.
|
||||
|
||||
## Rabbit holes de produto
|
||||
|
||||
- **Marcar no fechar em vez de no abrir.** Se alguém "só completar" com marcação ao fechar, o badge não zera na abertura — o usuário pediu explicitamente o contrário. **Corte:** marcar na abertura, ponto.
|
||||
- **Personalizar a lista.** Agrupar, filtrar, "não perturbe" vira outra superfície. **Corte:** apenas remover título/contador/botões e trocar o invólucro no desktop.
|
||||
|
||||
## Questões em aberto (produto)
|
||||
|
||||
- **A linha "X não lidas / Tudo em dia" do header some junto com o título?** **Opções:** A) remover — a contagem vive no badge do sino | B) manter. **Recomendação:** A — com a marcação automática ela vira sempre "Tudo em dia", sem função. _(assumido — validar)_
|
||||
- **Destaque visual de não lidas na lista:** após a marcação automática nunca aparece. **Opções:** A) remover | B) manter. **Recomendação:** A. _(assumido — validar)_
|
||||
|
||||
## Referências
|
||||
|
||||
- D2 — `docs/plans/notifications.md` (Issue #29, entregue; imutável — este item refina o sino dela, não edita o plano)
|
||||
- Canvas UI (gate): plan-c108-ui-draft.canvas.tsx
|
||||
- C94 — `docs/CHANGELOG-AGENTS.md` (2026-08-09): precedente Dialog/Drawer responsivo
|
||||
- `AGENTS.md` — shell do `/campanha` e leader lockdown
|
||||
Loading…
Reference in a new issue