Merge pull request #637 from fsolla/plans/plan-issue-33

plans(C114,C115): Google Calendar na agenda — espelho com notificação + edição bidirecional
This commit is contained in:
Francisco Solla 2026-08-11 00:39:32 +00:00 committed by GitHub
commit bfe3bf6b19
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 179 additions and 0 deletions

View file

@ -0,0 +1,92 @@
# C114 — Google Calendar: agenda da campanha espelhada com notificação (push Teqo → Google)
Status: rascunho
Atualizado em: 2026-08-10
Issue: #635
Priority: P1
Model: cursor-grok-4.5-medium
Impeccable: B — encaixe na agenda (junto do "link de import" atual)
Canvas UI: /home/fsolla/.cursor/projects/home-fsolla-cursor-worktrees-teqo-plans-plan-issue-33/canvases/plan-c114-ui-draft.canvas.tsx
Appetite: ~2 dias eng; um outcome verificável — a equipe vê a agenda da campanha no Google Calendar com atualização automática em minutos
## Intenção
O "link de import" (C16) entrega um espelho que o Google re-busca em cadência própria (horas de atraso, sem notificação — o defeito C113 é só a ponta). Isso não sustenta a agenda _oficial_ da campanha: a equipe e o candidato vivem no Google Calendar do celular. Com a API oficial do Google, o Teqo pode **escrever** os compromissos num calendário compartilhado da campanha: criar/remarcar/cancelar em Teqo aparece nos calendários de quem segue em minutos, e o Google cuida dos avisos — conforme as configurações de notificação de cada pessoa que segue, exatamente o que o pedido assume ("if their personal calendar is set to notify"). O Teqo continua a fonte da verdade.
## Persona e fluxo
- **Persona / contexto:** coordenação quer a agenda oficial da campanha; assessores e comunicação vivem no Google Calendar; candidato precisa ver os compromissos no celular sem entrar no Teqo.
- **Job principal:** "a agenda da campanha vive no meu Google Calendar e acompanha o Teqo — sem eu entrar no Teqo".
- **Fluxo desejado:** coordenador ativa a "Agenda da Campanha" no Google (uma vez, na agenda do Teqo) → a equipe adiciona o calendário pelo link (um clique) → criar/remarcar/cancelar em Teqo reflete no Google em minutos → quem segue recebe aviso conforme as próprias configurações de notificação; núcleo (candidato/coordenação) pode receber aviso garantido como participante.
- **Anti-goals de produto:** não é bidirecional (Google não volta — é C115); não substitui o link de import iCal (fica para quem não usa Google e para recortes filtrados); não é obrigar OAuth por pessoa (o modelo é calendário compartilhado, não escrever no calendário pessoal de cada um); Teqo continua SoT.
**Por que não OAuth por pessoa (decisão de produto, não tecnicismo):** escrever no calendário pessoal de cada staff via OAuth individual não gera notificação (o Google não avisa eventos no próprio calendário — aviso vem de ser participante), cria N cópias divergentes (remarcou um, o resto fica velho — o oposto de agenda oficial), custa N consentimentos/tokens/revogações (app não-verificado do Google expira token em dias) e quebra o RBAC do Teqo (o Google não conhece escopo por município nem leader lockdown; num calendário compartilhado, o access do Teqo vale). OAuth por pessoa só teria valor futuro na direção de _leitura_ (ver agenda pessoal p/ conflito de horário) — item separado.
### Esboço de fluxo (B)
```text
[agenda /campanha/agenda] → ativar "Agenda da Campanha" (configuração única)
→ link de adição para a equipe → criar/editar/cancelar em Teqo
→ push via API do Google (minutos) → calendário de quem segue atualiza + aviso conforme configs
```
## Objetivo e aceite
- Com um comando em `/campanha/agenda`, a campanha ativa um calendário Google compartilhado ("Agenda da Campanha") e o Teqo passa a receber o link público de adição para distribuir à equipe (Google assina direto; Apple/Outlook assinam a URL iCal que o Google expõe do mesmo calendário — atualização na cadência do cliente, sem notificação nativa).
- Compromissos criados/editados/cancelados em Teqo refletem no calendário Google em minutos (push via API oficial, não cadência de re-busca do assinante).
- Quem segue o calendário recebe notificação conforme as configurações de notificação do próprio Google; pessoas-chave podem ser participantes dos eventos (aviso garantido) — decisão em aberto abaixo.
- Teqo continua SoT e funciona sem Google: credencial inválida ou API fora não corrompe o Teqo — a UI mostra o estado do sync (sincronizado / pausado / não configurado) e re-tenta sem ação manual.
- A "Agenda da Campanha" espelha as atividades do escopo do staff (todas); o link de import iCal continua servindo recortes filtrados e não-Google.
- Sem PII desnecessária no evento (título, município, horário, local; sem lista de lideranças).
- Leader lockdown intocado (leader não vê nem ativa a integração).
## Dados (intenção)
- **Vou apresentar dados?** Não — a agenda é exportada para o calendário Google; a "forma" do evento é a do Google Calendar, decisão de apresentação do Google.
- **Decisões desbloqueadas:** nenhuma decisão de leitura no Teqo (o espelho é saída).
- **Forma:** _adiada_ — aqui só a restrição de produto "sem PII além do necessário no evento".
## Direção no codebase (hipótese)
- **Áreas prováveis:** agenda `/campanha/agenda` (superfície de ativação/estado), ações de servidor em `src/app/(campaign)/campanha/actions/`, utilitário de integração perto de `src/utilities/calendarFeed.ts` (precedente do feed), envs de credencial Google no Vercel (precedente `REVALIDATE_SECRET`/Blob).
- **Precedente a olhar:** plans C16/C92/C93/C96/C98/C113 (link de import e feed iCal); collection `calendarFeed` como precedente de recurso de sync com credencial revogável.
- **Risco de acoplamento:** credenciais de conta Google da campanha em produção (service account) — segredo bem guardado, sem vazar em log; falha da API não pode derrubar a agenda do Teqo; nenhum dado pessoal duplicado no Google.
## Dependências
- Nenhuma dura. Suaves: C113 (frescor do iCal) é independente — o push cobre consumidores Google; o iCal segue para o resto e para recortes.
## Fora de escopo
- Bidirecional (edição no Google volta para o Teqo) → **C115**.
- OAuth por pessoa (cada staff autoriza o Teqo a escrever no calendário pessoal dele): anti-goal explícito — o modelo é calendário compartilhado.
- Um calendário por filtro/recorte: o link de import iCal já cobre; a Agenda da Campanha é o espelho cheio.
- Migração de eventos de outras fontes (agendas antigas) para o Google.
- Notificação intra-TeQo (sino) — já existe; este item não mexe.
## Rabbit holes de produto
- **"Um calendário por assessor/município."** Se alguém "só completar": explodir em N calendários e permissões. **Corte neste item:** um calendário oficial compartilhado; recortes seguem no iCal.
- **"Sincronizar todo o histórico."** **Corte:** janela de eventos (passado próximo + futuro), como o feed já faz.
- **"Notificação garantida para todo mundo."** **Corte:** quem segue recebe conforme as próprias configurações (é o que o pedido assume); garantia só para o núcleo (participantes), se a mesa quiser.
## Questões em aberto (produto)
- **Quem é o dono da conta do calendário?** **Opções:** A) conta Google da campanha (Gmail/Workspace existente): o calendário nasce nela e o Teqo escreve com credencial de service account autorizada naquele calendário | B) o service account cria o calendário (dono = service account; transferência de dono depois é difícil). **Recomendação:** **A** — o Google recomenda não usar service account como dono; a campanha provavelmente já tem conta própria. _(assumido — validar: existe conta Google da campanha? qual?)_
- **Notificação da equipe: só seguir o calendário ou também participar?** **Opções:** A) todo mundo segue o calendário (aviso conforme configurações de cada um) | B) núcleo (candidato/coordenação) entra como participante dos eventos (aviso garantido por e-mail/app; evento também aparece no calendário pessoal dele). **Recomendação:** **B para o núcleo, A para o resto** — a agenda oficial merece aviso garantido para quem decide. _(assumido — validar)_
- **O que vai no evento do Google?** **Opções:** A) título, município, horário, local, tags | B) só título + horário. **Recomendação:** **A**, sem lista de lideranças e sem telefones. _(assumido — validar)_
- **Onde fica o estado do sync na UI?** **Recomendação:** pill no header da agenda ("Google: sincronizado/pausado") + aviso claro ao ativar sem credenciais configuradas. _(assumido — validar)_
- **Visibilidade do calendário compartilhado?** **Opções:** A) público no link (qualquer um com o link vê; o link é a credencial, como o secret do feed iCal atual) | B) só pessoas convidadas individualmente (exige conta Google de cada um — quem usa Apple sem conta Google não assina). **Recomendação:** **A** — a equipe é mista (Google + Apple); o link público cobre os dois e o conteúdo já é o da agenda oficial (sem PII, por guardrail). Quem quiser mais restrição compartilha o link internamente. _(assumido — validar)_
## Decisões travadas (gate)
- Modelo = calendário compartilhado da campanha com escrita via API oficial (não OAuth por pessoa, não iCal).
- Direção única Teqo → Google neste item; Teqo permanece SoT.
- A Agenda da Campanha é o espelho cheio do escopo do staff; recortes ficam no iCal.
## Referências
- GitHub Issue [#635](https://github.com/fsolla/teqo/issues/635)
- Canvas UI (gate): [plan-c114-ui-draft.canvas.tsx](/home/fsolla/.cursor/projects/home-fsolla-cursor-worktrees-teqo-plans-plan-issue-33/canvases/plan-c114-ui-draft.canvas.tsx)
- Plans C16 (`sync-teqo-google-calendar.md`), C113 (`c113-feed-ical-agenda-nao-atualiza.md`), C92/C93/C96/C98
- `src/utilities/calendarFeed.ts`, `src/collections/CalendarFeed.ts`, `src/components/campaign/activity/CalendarFeedDialog.tsx`

View file

@ -0,0 +1,87 @@
# C115 — Google Calendar: edição pela agenda pessoal volta para Teqo (bidirecional)
Status: rascunho
Atualizado em: 2026-08-10
Issue: #636
Priority: P2
Model: cursor-grok-4.5-high
Impeccable: B — encaixe na agenda (gestão/estado do sync)
Canvas UI: /home/fsolla/.cursor/projects/home-fsolla-cursor-worktrees-teqo-plans-plan-issue-33/canvases/plan-c115-ui-draft.canvas.tsx
Appetite: ~2–2,5 dias eng; um outcome verificável — candidato remarca um compromisso no Google e a agenda inteira da equipe acompanha
## Intenção
Com o espelho do C114, o Teqo escreve no Google — mas a agenda só é _oficial_ de verdade quando o caminho inverso existe: o candidato (e pessoas escolhidas) editam o compromisso na própria agenda do Google — remarcar horário, mudar título, cancelar — e isso volta para o Teqo e propaga para todo mundo que segue, com notificação. O Google Calendar vira um segundo ponto de entrada da agenda da campanha, não só um espelho. Sem isso, quem decide continuaria preso ao Teqo ou ficaria com a agenda divergente.
## Persona e fluxo
- **Persona / contexto:** candidato em trânsito, só o celular, sem entrar no Teqo; coordenação autoriza quem pode editar pelo Google.
- **Job principal:** "remarquei no Google; a campanha inteira viu".
- **Fluxo desejado:** candidato abre o Google Calendar (a Agenda da Campanha está na lista de calendários dele) → arrasta/edita/cancela um compromisso → o Teqo detecta (notificação de push do Google + sync incremental) → atualiza a atividade → propaga para todos os calendários sincronizados com aviso conforme configurações.
- **Anti-goals de produto:** criação de eventos novos pelo Google fora de v1 (atividade exige município — dado que o Google não tem); conflito não vira tela de resolução manual; ninguém além do grupo autorizado edita pelo Google; leader lockdown intocado.
### Esboço de fluxo (B)
```text
[candidato no Google Calendar] → edita/remarca/cancela evento da Agenda da Campanha
→ webhook do Google avisa Teqo → sync incremental busca a mudança
→ atividade atualizada no Teqo → push para todos os seguindo (C114) + notificação
```
## Objetivo e aceite
- Pessoas autorizadas (decisão em aberto: candidato e/ou coordenação) editam data/hora/título/cancelamento de compromissos existentes no Google Calendar e a mudança reflete na atividade do Teqo e nos calendários de todos os que seguem, com notificação (mesmo mecanismo do C114).
- Campos que são invariantes estruturais do Teqo (município, tipo, status interno) **não** são editáveis pelo Google: tentativa não propaga (decisão em aberto: ignorar ou reverter no Google).
- Detecção robusta: mudanças feitas no Google chegam ao Teqo mesmo com o app fechado; o canal de notificação se renova sozinho (a API exige renovação periódica); falha do Google não quebra o Teqo — re-tenta e mostra estado.
- Conflito (mesmo evento editado no Teqo e no Google): regra simples e documentada (recomendação: vence a edição mais recente, com registro) — nunca uma tela de resolução manual em v1.
- Apenas quem está autorizado edita pelo Google; leader segue sem acesso; Teqo continua funcionando sem Google.
## Dados (intenção)
- **Vou apresentar dados?** Não — sincronização de eventos; a "forma" é o evento do Google Calendar.
- **Decisões desbloqueadas:** nenhuma leitura nova no Teqo.
- **Forma:** _adiada_.
## Direção no codebase (hipótese)
- **Áreas prováveis:** rota pública de webhook do Google **fora de `(app)`** (precedente: `agenda/ical/[secret]`), utilitário de sync perto de `src/utilities/calendarFeed.ts`, ações de `src/app/(campaign)/campanha/actions/`, agenda `/campanha/agenda`.
- **Precedente a olhar:** C114 (espelho — base obrigatória), `agenda/ical/[secret]` (rota pública sem cookie com credencial), C96 (fail-closed no escopo do criador), padrões de token de campanha (validação de origem).
- **Risco de acoplamento:** webhook público é superfície nova — validar origem (headers `X-Goog-*` + token de canal), nunca confiar no corpo sem validação; edição vinda do Google precisa respeitar access (quem editou = quem foi autorizado; não propagar fora do escopo); leader nunca recebe escrita do Google.
## Dependências
- **Dura: C114** (espelho Teqo → Google; este item adiciona a volta). Suaves: C113.
## Fora de escopo
- Criar atividade nova a partir do Google (v2 — precisa resolver os campos obrigatórios que o Google não tem, ex.: município).
- Resolução manual de conflito (tela): regra automática simples + registro.
- Bidirecional para outras plataformas (Apple/Outlook): o link de import iCal segue só-leitura.
- Permissões por evento (quem edita é definido por calendário, não por evento, em v1).
## Rabbit holes de produto
- **"Resolver conflito por evento com tela."** Se alguém "só completar": admin de conflito inteiro. **Corte:** regra automática (mais recente vence) + registro auditável.
- **"Importar criação arbitrária do Google."** **Corte:** sem município não entra; v2.
- **"Sincronizar recorrências e todos-os-dias complexos."** **Corte:** o mapa de campos é o do espelho (C114); fora do aceite.
## Questões em aberto (produto)
- **Quem pode editar pelo Google?** **Opções:** A) só candidato | B) candidato + coordenador | C) lista de staff escolhida pela coordenação. **Recomendação:** **B** — o candidato é o caso do pedido; a coordenação precisa do mesmo direito para correções rápidas. _(assumido — validar)_
- **Regra de conflito (mesmo evento editado nos dois lados):** **Opções:** A) vence a edição mais recente (relógio), com registro | B) Teqo sempre vence | C) a mudança não propaga até alguém decidir (pausa o evento). **Recomendação:** **A** — simples e previsível no ritmo da campanha; B trai o sentido de "editar pelo Google"; C vira a tela de conflito que cortamos. _(assumido — validar)_
- **Edição pelo Google de campo estrutural (ex.: município):** **Opções:** A) a edição não propaga (o Google reverte na próxima sincronização) | B) propaga e o Teqo rejeita com aviso. **Recomendação:** **A** — para campos estruturais, o Google é espelho do Teqo. _(assumido — validar)_
- **Como o staff autorizado ganha permissão de escrita no calendário?** Operacional (convite de permissão pelo próprio Google na ativação do C114), não é código — mas o Teqo precisa mostrar quem está autorizado e permitir desautorizar.
## Decisões travadas (gate)
- Direção inversa Google → Teqo só para **editar/remarcar/cancelar atividades existentes** (sem criação em v1).
- Regra de conflito automática (nunca tela manual em v1).
- Acessos do Teqo (incluindo leader lockdown) valem também para o que vem do Google.
- **Teqo permanece a fonte de verdade do domínio:** o Google é superfície de edição (horário/título/cancelamento) + projeção — nunca autoritativo. O evento no Google carrega vínculo com a atividade; a edição vinda de lá _vira_ edição no Teqo e o Teqo **reescreve o evento a partir do próprio estado** (idempotente, corrigindo campos estruturais que o Google tentar mudar). Se o Google desligar, a agenda do Teqo fica intacta; se o Teqo desligar, o calendário é um snapshot congelado.
## Referências
- GitHub Issue [#636](https://github.com/fsolla/teqo/issues/636)
- Canvas UI (gate): [plan-c115-ui-draft.canvas.tsx](/home/fsolla/.cursor/projects/home-fsolla-cursor-worktrees-teqo-plans-plan-issue-33/canvases/plan-c115-ui-draft.canvas.tsx)
- Plans C114 (espelho), C16/C92/C93/C96/C98, C113
- `src/app/(campaign)/campanha/agenda/ical/[secret]/route.ts`, `src/utilities/calendarFeed.ts`