mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 15:13:07 +00:00
Compile the roadmap into next-steps docs and align project references.
Replace the delivery diary with open items and windows, and sync AGENTS, notebook, README, and plan pointers with provisional Onda 0 Consent hold.
This commit is contained in:
parent
429b31048f
commit
71421c725c
25 changed files with 244 additions and 387 deletions
|
|
@ -7,49 +7,21 @@ alwaysApply: false
|
|||
|
||||
## Status
|
||||
|
||||
- **MVP Ciclo 1 entregue e enviado em `main` (2026-07-18):** ondas 1–8 (auth/RBAC, domínio+UI de Núcleo/Liderança/Inteligência/Atualizações/Convites, dashboard, hardening) + refactors de composição Next.js, primitivos compartilhados, locks PostgreSQL, fixtures E2E e migration consolidada `20260718_010733_consolidate_campaign_schema`.
|
||||
- **Ciclo 2 entregue, mesclado e deployado (2026-07-18):** A1 território multi-município/bairro, A3 baseline TSE 2022 (modelo + import), B1 overview da lista, C1 compartilhar página e D1 PWA — detalhes no item datado abaixo.
|
||||
- **A2 entregue e mesclado em `main` (2026-07-18):** cadastro estático município↔ZE (`bahiaTseZones`, TSE 2024) + chips opt-in no formulário (`NucleusTerritoryAndZonesFields` / `territorySuggestions`); sem migration. Plano: [`docs/plans/zonas-por-municipio.md`](../../../docs/plans/zonas-por-municipio.md).
|
||||
- **A4 Baseline no produto + Gap vs 2022 implementado e mesclado em `main` (2026-07-18):** agregação `getNucleusElectoralBaseline` / `loadNucleusBaseline2022Overview` + `computeGapVs2022`; UI `NucleusElectoralBaseline` + `NucleusInsights` na aba Visão geral; card "Baseline 2022" no overview da lista; chapa via `BASELINE_TICKET_2022`. Sem migration. Simplify no PR; débitos de escala → **A7** ([`escala-dry-pos-a4.md`](../../../docs/plans/escala-dry-pos-a4.md)). Plano pai: [`baseline-eleitoral-tse.md`](../../../docs/plans/baseline-eleitoral-tse.md).
|
||||
- **A7 Escala e DRY pós-A4 registrado (2026-07-19):** agregar ranking federal no detalhe, filtro por `cityCode` TSE, Alert `confirmed` + `Progress`. Plano: [`docs/plans/escala-dry-pos-a4.md`](../../../docs/plans/escala-dry-pos-a4.md).
|
||||
- **C3 Planos de Ação implementado (2026-07-18):** collection `actionPlan` + vertical `/campanha/planos` (lista com tabs Próximos/Todos/Realizados/Rascunhos, detalhe com tabs Visão geral/Tarefas/Atualizações, forms novo/editar), blocos "Próximos eventos" no overview de núcleos e no dashboard; `startAt` opcional só em rascunho; access por `coordinators`/`leadership`; migration `20260718_222832_add_action_plan`.
|
||||
- **B2 Mapa Fase 1 (geometrias) entregue (2026-07-18):** TopoJSON estático municípios IBGE (`bahia-municipalities.topo.json`, ~132 KB, object `municipalities`) + TIs por dissolução (`bahia-identity-territories.topo.json`, ~15 KB, object `territories`); tabela `bahiaMunicipalityCodes` + `codeForMunicipality` / `municipalityForCode`; helpers `bahiaGeometries` (`getMunicipalityFeature` / `getTerritoryFeature`); script re-executável `pnpm build:geometries` (cache `data/geometries/`, sem DB); testes int de bijeção/cobertura/teto de bytes. Sem migration, sem UI (Leaflet = B3). Plano as-built: [`docs/plans/mapa-bahia-geometrias.md`](../../../docs/plans/mapa-bahia-geometrias.md).
|
||||
- **B5 registrado (2026-07-18):** débitos de escala/DRY pós-B2 do `/simplify` que não entraram no cleanup (lazy load mun/TI + helper `ensureCachedDownload` para `build:geometries` / `db:seed:tse`) — plano [`docs/plans/escala-dry-pos-b2.md`](../../../docs/plans/escala-dry-pos-b2.md); F1 preferencialmente com B3; F2 cortável.
|
||||
- **C7 registrado (2026-07-18) / entregue (2026-07-19):** débitos de escala/DRY pós-C3 do `/simplify` — plano [`docs/plans/escala-dry-pos-c3.md`](../../../docs/plans/escala-dry-pos-c3.md); composição territorial, shell assíncrono de contato, leituras por aba, short-circuit de hooks. Débitos pós-`/simplify` → **C11** ([`docs/plans/escala-dry-pos-c7.md`](../../../docs/plans/escala-dry-pos-c7.md)).
|
||||
- **Fill-in reset de senha + foto de perfil (2026-07-19):** MVP implementado — plano [`docs/plans/reset-senha-foto-perfil.md`](../../../docs/plans/reset-senha-foto-perfil.md) (trocar senha logado + forgot por e-mail Resend + fallback convite; `/campanha/perfil` com avatar). Débitos pós-`/simplify` → **RS+** ([`escala-dry-pos-reset-senha-perfil.md`](../../../docs/plans/escala-dry-pos-reset-senha-perfil.md)): auth read `depth: 0`, avatar fora da TX, `CampaignAuthPageShell`, schemas de senha unificados.
|
||||
- **Trilha E registrada (2026-07-19) — mapa de projeção por município:** plano E1–E5 para equiparar e superar as planilhas "Mapa de projeção de votos Solla 2026" (`docs/sheets/`): E1 metas em cenários (`voteGoals` Bom/Regular/Mínimo) + `priority` no núcleo (migration compartilhada com E3), E2 série histórica TSE 2014/2018 + `computeVoteTrend` derivado (sem migration), E3 `dobradinhaNotes`/`nextSteps` staff-only na inteligência, E4 import único `pnpm db:seed:mapa` (sem criar `Contact`/`leadership`), E5 Salvador por bairro registrado como futuro. Plano: [`docs/plans/mapa-projecao-municipios.md`](../../../docs/plans/mapa-projecao-municipios.md).
|
||||
- **E1+E3 entregues e mesclados em `main` (2026-07-19):** migration `20260719_054522_add_nucleus_goals_strategy` (`voteGoals` Bom/Regular/Mínimo + `priority` `alta|normal` + `dobradinhaNotes`/`nextSteps` staff-only); card "Metas 2026" no detalhe, filtro `?priority=alta`, somas no overview B1 e KPI no dashboard; edição de dobradinhas/encaminhamentos em `NucleusIntelligenceDialog` + cards staff na Visão geral; campos E3 excluídos dos view models de `lideranca`. Domínio em `voteGoals.ts`; débitos pós-`/simplify` → **E6** ([`escala-dry-pos-e1.md`](../../../docs/plans/escala-dry-pos-e1.md)).
|
||||
- **E2 entregue (2026-07-19):** seed TSE parametrizado (`pnpm db:seed:tse -- --year=2014|2018|2022`; anos históricos só dep. federal T1 BA), `computeVoteTrend` ±10% (`increase|stable|decline|noBaseline`), loader candidate-only multi-ano, série + badge no `NucleusElectoralBaseline`, Alert de tendência no `NucleusInsights`, card "Tendência histórica" no overview da lista. Sem migration.
|
||||
- Fluxos operacionais cobertos: login isolado, CRUD/arquivo de núcleos por slug, designação de coordenador (`geral`), CRM de lideranças, estimativas sugerir/confirmar com versão UUID, feed de atualizações, convites WhatsApp (`wa.me` + hash de uso único), dashboard com agregados só de núcleos ativos.
|
||||
- Pendência operacional (não de código): textos provisórios Onda 0 provisionados por migration ([`docs/plans/onda-0.md`](../../../docs/plans/onda-0.md)); substituição pela assessoria jurídica eleitoral + hold de PII real de lideranças/apoiadores até lá; smoke pós-deploy Vercel. Ver Onda 0 em [`docs/roadmap.md`](../../../docs/roadmap.md).
|
||||
- Em 2026-07-17, a listagem de Núcleos passou a aplicar filtros automaticamente pelo estado da URL, sem botão de confirmação, com comboboxes territoriais estritos compartilhados com o formulário.
|
||||
- Em 2026-07-17, o contato principal passou a usar busca remota limitada e escopada no detalhe do núcleo, sem serializar a lista completa de lideranças.
|
||||
- Em 2026-07-17, o feed paginado de atualizações e a prévia das três mais recentes passaram a usar loaders separados por aba, sem consulta de prévia fora da visão geral.
|
||||
- Em 2026-07-17, o painel de lideranças passou a restaurar foco deterministicamente no fechamento de Sheet/Drawer e ganhou E2E real de URL, histórico, responsividade e privacidade de Flight.
|
||||
- Em 2026-07-17, o login de campanha removeu React Hook Form/Zod do bundle cliente em favor de formulário nativo com `useActionState`; `Table` voltou a ser módulo estático sem fronteira cliente.
|
||||
- Em 2026-07-17, os filtros da lista passaram a montar uma única árvore de controles em um disclosure responsivo; o estado canônico vindo do servidor remonta a ilha por chave de query, sem sincronização por `useSearchParams`.
|
||||
- Em 2026-07-17, a composição Phase 1 separou o cartão estático de coordenação no servidor dos controles cliente; opções elegíveis e revisão do Núcleo agora são carregadas somente ao abrir o seletor. Editores pesados de inteligência, estimativa, atualização, liderança e convite passaram a fronteiras lazy locais.
|
||||
- Em 2026-07-17, os shells lazy passaram a manter seus gatilhos Radix montados durante carregamento e edição, com restauração real de foco validada no navegador; o carregamento de coordenação permanece cancelável e só bloqueia fechamento durante a gravação.
|
||||
- Em 2026-07-17, falhas nos chunks lazy de inteligência, atualização, convite e estimativa ganharam estado de erro acessível, fechamento seguro e nova tentativa controlada sem rejeição não tratada ou spinner infinito.
|
||||
- Em 2026-07-17, o fluxo lazy de estimativa passou a transportar uma intenção explícita de foco através do refresh RSC: sugestão foca revisão, confirmação e edição focam o gatilho de editar, com fallback estável no cartão.
|
||||
- Em 2026-07-17, a Refactor Phase 2C centralizou os advisory locks transacionais PostgreSQL e separou namespaces por domínio, com regressões concorrentes validadas.
|
||||
- Em 2026-07-17, o follow-up Phase 2C tornou `contact-phone:<normalizado>` um invariante único para todo create/update de `Contact`; mudanças bloqueiam telefones antigo+novo em ordem determinística, rejeitam outro titular sem merge e preservam o convite em rollback.
|
||||
- Em 2026-07-17, a Refactor Phase 2D reduziu `actions/invite.ts` a uma orquestração de 44 linhas e separou repositório SQL, criação e resgate em módulos `server-only`, preservando 488 testes de integração.
|
||||
- Em 2026-07-17, a Refactor Phase 4A consolidou as dez migrations locais não publicadas de `20260717_*` em `20260718_010733_consolidate_campaign_schema`, mantendo intacta a história rastreada até `20260716_010420_add_campaign_user`; os bancos locais foram reconciliados sem alterar dados de aplicação.
|
||||
- Em 2026-07-18, o follow-up Phase 4C fixou o Playwright em dois workers por padrão, adicionou guardas automáticos de erros do navegador/console/5xx, fortaleceu privacidade HTML+Flight por papel e tornou o login por teclado uma interação real com Enter e cookie verificado.
|
||||
- Em 2026-07-18, a projeção do dashboard omite `supportStatus` ausente por redação de acesso e lança invariante genérico se um valor não-vazio inválido chegar à fronteira; agregação e badge são exaustivos no enum.
|
||||
- Em 2026-07-18, os designs UX Pilot dos próximos ciclos foram versionados em `docs/design-refs/latest/` (11 pares `.png`+`.html`) e mapeados aos itens do roadmap — tabela "Referências de design (UX Pilot)" em `docs/roadmap.md`, com seção "Referência visual (UX Pilot)" em cada plano. Decisão: a UX/estrutura dos designs é a referência; a paleta NÃO — os arquivos vieram na paleta antiga (vermelho escuro/navy/dourado) e toda implementação usa os tokens claros do tema `data-theme='campaign'`. Sem design nesta leva: dobradinha, mapa, demandas, detalhe do plano de ação, opt-in de push e fill-ins.
|
||||
- **Em 2026-07-18, o Ciclo 2 foi mesclado e deployado: A1, A3, B1, C1 e D1.**
|
||||
- **B1 Overview da lista** — painel agregado entre filtros e lista em `/campanha/nucleos` (`NucleusListOverview`, `loadNucleusListOverviewData`): estimativa de votos confirmada + % + sugestões pendentes (staff), cobertura de coordenador e prévia de 3 atualizações, tudo sobre o conjunto **filtrado** inteiro (`buildNucleusListWhere`, `pagination: false`), oculto quando vazio. `lideranca` vê o overview com select reduzido, sem contagem de sugestões e só as próprias atualizações.
|
||||
- **C1 Compartilhar núcleo** — `ShareNucleusDialogShell` no header do detalhe; destinatários carregados sob demanda por Server Action de leitura (`loadNucleusShareRecipients`), não no DTO da página. Loader confirma acesso ao núcleo (`overrideAccess: false`) e então lê nome+telefone com `overrideAccess: true` (evita N+1 de field access); lideranças só `engajado`; seção de lideranças omitida para o papel `lideranca`; `canReadCampaignUserPhone` passou a liberar telefone de `geral` para qualquer usuário de campanha. Compartilhar envia só o link — não cria convite nem concede acesso.
|
||||
- **D1 PWA** — manifest + SW servidos por route handlers sob `/campanha` (script em `campaignPwa.ts`), cache `campanha-<buildId>` com `skipWaiting`/`claim`, navegação network-first com fallback `/campanha/offline`, RSC/Flight e `/campanha/convite/*` jamais cacheados; ícones em `public/campaign-icons/`; `InstallPwaToast` (Android `beforeinstallprompt`, iOS Drawer, dispensa em `sessionStorage`); logout limpa Cache API no cliente antes da action.
|
||||
- **A3 Baseline TSE 2022 Fase 1** — collections `electionTally`/`electionCandidateVote`/`electionCandidate` (grupo `Dados Eleitorais`, leitura para `campaignUser`/`users` via `canReadElectionData`, escrita admin-only), migration `20260718_195854_add_election_results`, seed local `pnpm db:seed:tse` (idempotente por escopo year/office/turn, delete+bulk insert via drizzle, guard de banco local). Sem PII (sem CPF/título); identidade cross-ano por `identityKey` hash. (UI do baseline = A4, item de status acima.)
|
||||
- **Em 2026-07-18, C2 Cadastro nominal de apoiadores — engenharia pronta e mesclada em `main` (pendente deploy + lote jurídico).** Collection `supporter` (join `Contact`↔campanha, núcleo opcional), migration `20260718_222656_add_supporter` com `UNIQUE NULLS NOT DISTINCT (contact_id, nucleus_id)`, consent por chaves `apoiador-cadastro` / `apoiador-intencao-voto` via `campaignConsent.ts` genérico, actions (create / intenção / import CSV só `geral` / remover dados), UI `/campanha/apoiadores` (lista+KPIs, ficha, wizard import, kit mínimo `wa.me`). Telefone obrigatório; `lideranca` sem acesso à área. Produção com dados reais espera deploy (build Vercel aplica a migration) + Consent keys + aprovação jurídica (Onda 0).
|
||||
- **Em 2026-07-18, C6 Escala e DRY pós-C2 registrado no roadmap** (após `/simplify` do C2): shells compartilhados com listas de núcleos, import bulk drizzle + skip phone-invariant, preview token, KPI agregado SQL, StrictCombobox/AlertDialog. Plano: [`docs/plans/escala-dry-pos-c2.md`](../../../docs/plans/escala-dry-pos-c2.md). Depende do merge de C2; fases 3–5 cortáveis se a base permanecer pequena.
|
||||
- **Em 2026-07-19, C9 Escala e DRY pós-C8 entregue:** `supporterListFilters.ts` (filtro unificado Payload↔SQL + `contactSearchQuery`), `loadSupportersPageData` (1× lookup de núcleos do coordenador), `apoiadores/[id]/formActions` no mapper compartilhado, `fieldError` nos 8 componentes de form restantes. Plano: [`docs/plans/escala-dry-pos-c8.md`](../../../docs/plans/escala-dry-pos-c8.md).
|
||||
- **Em 2026-07-19, C10 Escala e DRY pós-C9 registrado** (após `/simplify` do C9): dedup de access na lista (`req.context`), query única de núcleos do coordenador, `errorProps` nos forms grandes. Plano: [`docs/plans/escala-dry-pos-c9.md`](../../../docs/plans/escala-dry-pos-c9.md).
|
||||
- **Em 2026-07-19, C11 Escala e DRY pós-C7 registrado** (após `/simplify` do C7): feed O(n) condicional, loaders do detalhe + `loadCampaignUserNamesById`, selects por aba, hot paths mutação/form, utilities de busca/label. Plano: [`docs/plans/escala-dry-pos-c7.md`](../../../docs/plans/escala-dry-pos-c7.md).
|
||||
- **Em 2026-07-19, Field Desk polish (Impeccable) + débitos FD+/FD2:** quieter→polish em `/campanha` (critique 28→32); shells/strips/freshness/filas-first. Débitos `/simplify` → **FD+** ([`escala-dry-pos-field-desk.md`](../../../docs/plans/escala-dry-pos-field-desk.md)); débitos de UX/produto do critique não implementados → **FD2** ([`field-desk-ux-pos-critique.md`](../../../docs/plans/field-desk-ux-pos-critique.md)).
|
||||
Próximos / janelas / grafo: **[`docs/roadmap.md`](../../../docs/roadmap.md)** (compilado 2026-07-19 — documento de próximos passos, não diário).
|
||||
|
||||
**Já em `main` (resumo):**
|
||||
|
||||
- **Núcleos MVP + Ciclo 2** — auth/`campaignUser`, A1 território multi, A2 zonas+chips, A3/A4 baseline+gap, B1 overview, B2 geometrias, C1 share, C3 agenda, D1 PWA.
|
||||
- **Operação** — C2 cadastro nominal (eng. pronta; **dados reais ↔ Onda 0**), C6–C9 escala/DRY. Abertos: C10, C11, C4, C5.
|
||||
- **Trilha E (parcial)** — E1+E3 metas/estratégia manual; E2 série 2014/2018+tendência. Abertos: E4–E7. Plano: [`mapa-projecao-municipios.md`](../../../docs/plans/mapa-projecao-municipios.md).
|
||||
- **Fill-ins** — Visitados recentemente; Reset senha + foto de perfil. Débitos: VR+, RS+. Field Desk polish em branch → FD+/FD2.
|
||||
- **Débitos escala registrados** — A7, A8, B5, O0+ (ver roadmap Próximos).
|
||||
|
||||
**Onda 0:** textos provisórios provisionados ([`onda-0.md`](../../../docs/plans/onda-0.md)); hold de PII real + lote jurídico final; smoke pós-deploy. Detalhe no roadmap § Onda 0.
|
||||
|
||||
Fluxos operacionais cobertos: login isolado, CRUD/arquivo de núcleos por slug, coordenador, CRM de lideranças, estimativas UUID, atualizações, convites `wa.me`, dashboard, apoiadores (eng.), planos de ação, baseline/metas/tendência na UI.
|
||||
|
||||
Designs UX Pilot: `docs/design-refs/latest/` — UX/estrutura sim, paleta não (`data-theme='campaign'`). Tabela de refs abertas no roadmap.
|
||||
|
||||
## Decisões
|
||||
|
||||
|
|
@ -115,5 +87,4 @@ alwaysApply: false
|
|||
## Bloqueadores e próximas frentes
|
||||
|
||||
- **Bloqueador obrigatório de produção (PII real):** antes de lideranças/apoiadores reais ou import em massa, a assessoria jurídica eleitoral deve aprovar os textos definitivos (substituindo os provisórios do [Onda 0 MVP](../../../docs/plans/onda-0.md)). Os textos provisórios já sobem automaticamente via migration; o hold é de **titulares reais**, não de engenharia.
|
||||
- Em 2026-07-17 o roadmap foi repriorizado orientado ao calendário eleitoral (Res. TSE 23.760/2026: propaganda a partir de 16/08, 1º turno 04/10), com ordem de execução, trilhas paralelas (A dados eleitorais/território, B superfícies de coordenação, C operação de campo, D plataforma) e grafo de dependências. Decisões de sequenciamento: lançar o MVP antes da migração multi-município; multi-município antes de zonas-por-municipio/eventos/demandas; baseline TSE independente de zonas-por-municipio (dependência rebaixada a suave); WhatsApp Business API movido a fora de escopo (vedação Meta + Res. 23.610 art. 33).
|
||||
- Backlog consolidado do produto (admin RBAC, mapa, eventos, demandas, apoiadores, listas globais, site, white-label): [`docs/roadmap.md`](../../../docs/roadmap.md).
|
||||
- **Próximos passos** (janela 1 vigente, grafo, cortes): [`docs/roadmap.md`](../../../docs/roadmap.md) — compilado 2026-07-19. Não recontar entregas aqui; decisões travadas ficam em Decisões acima + AGENTS.md.
|
||||
|
|
|
|||
20
AGENTS.md
20
AGENTS.md
|
|
@ -31,20 +31,16 @@ Teqo is the digital platform for deputado federal Jorge Solla (PT-BA): public si
|
|||
- To (re)populate news content, `pnpm db:seed:posts` fetches articles live from jorgesolla.com.br into the LOCAL db (same non-local `DATABASE_URL` guard as `pnpm dev`). It writes from a CLI process outside the deployed runtime, so after seeding — or after ANY direct-DB change — you must bust the production `posts` cache via `POST /api/revalidate`. See "Posts & Tags" below.
|
||||
- To (re)populate the TSE 2022 election baseline, `pnpm db:seed:tse` downloads TSE open data and imports the Bahia scope into the LOCAL db (same guard; no revalidate needed — campaign pages are dynamic). See "Election baseline data (TSE 2022)" below.
|
||||
|
||||
## Known Gaps (as of 2026-07-18 — resolve before relying on this file for onboarding new devs)
|
||||
## Known Gaps (as of 2026-07-19 — resolve before relying on this file for onboarding new devs)
|
||||
|
||||
Backlog consolidado (bloqueadores, site, campanha, white-label): [`docs/roadmap.md`](docs/roadmap.md).
|
||||
Backlog consolidado (próximos passos, bloqueadores, site, campanha, white-label): [`docs/roadmap.md`](docs/roadmap.md). Status operacional de Núcleos: [`.cursor/rules/projects/nucleos-eleitorais.mdc`](.cursor/rules/projects/nucleos-eleitorais.mdc).
|
||||
|
||||
1. **Payload admin RBAC is still absent.** The campaign auth collection now has `role`, but the `users` admin collection still has no roles; every admin user has full Payload admin access. Add admin roles before opening `/admin` to a broader team.
|
||||
2. **Legacy public consent flows still hardcode document IDs** (e.g. `consent: 2` in `submitWhatsapp.ts`). The campaign leadership flow does not: it requires `Consent.key = 'lideranca-autopreenchimento'` and fails closed when missing. Migrating the older public flows remains separate work.
|
||||
3. **`Post`/`Tag` ship the news system, but there is still no `Pages` collection for institutional content.** Partly resolved: the `post`/`tag` collections back the news/publications system, and the home "Últimas notícias" list plus the `/[type]`, `/[type]/[category]`, and article routes are live (see "Posts & Tags" below). Still hardcoded/pending: the home hero heading + subtitle copy (`HomePage` global still exposes only a single `image` field — the text lives in `src/app/(frontend)/(home)/page.tsx`), and there is no `Pages` collection yet for institutional content such as the bio and propostas.
|
||||
4. **Campaign LGPD consent texts are not yet configured in production.** Invites and self-service leadership flows fail closed until an admin creates `Consent.key = 'lideranca-autopreenchimento'` with counsel-approved copy. The C2 supporter registry (merged to `main`, production-blocked) additionally requires `Consent.key = 'apoiador-cadastro'` and `Consent.key = 'apoiador-intencao-voto'` (vote intention is sensitive data) — same fail-closed behavior. All four keys are bundled in the single legal batch of Onda 0 (see deploy checklist).
|
||||
4. **Campaign LGPD — textos finais + PII real (Onda 0).** Provisórios MVP já provisionados por migration / `pnpm db:seed:consent` (`lideranca-autopreenchimento`, `apoiador-cadastro`, `apoiador-intencao-voto`, `campanha-notificacoes-push` + global `/privacidade`). O app ainda falha fechado se as chaves faltarem. **Hold operacional:** não cadastrar titulares reais nem importar base nominal em massa até a assessoria jurídica eleitoral aprovar e substituir os textos definitivos (lote único — ver [`docs/plans/onda-0.md`](docs/plans/onda-0.md) e checklist abaixo). C2 engenharia está em `main`; dados reais ↔ Onda 0.
|
||||
|
||||
**Recently resolved (2026-07-18):** the `/campanha` nuclei MVP shipped (auth/RBAC, nuclei, leaderships, vote estimates, updates, WhatsApp invites, dashboard) with consolidated migration `20260718_010733_consolidate_campaign_schema`. Later the same day, cycle 2 was merged and deployed: multi-municipality/neighborhood territory arrays (migration `20260718_190559_territorio_multi_municipio_bairro`), TSE 2022 election baseline collections + `pnpm db:seed:tse` (migration `20260718_195854_add_election_results`), the filtered nuclei-list overview panel, the nucleus share dialog, and the installable `/campanha` PWA. Still on 2026-07-18, **A2** shipped: static Bahia municipality↔TSE zone map (`src/lib/bahiaTseZones.ts` from TSE 2024 `detalhe_votacao_munzona` BA) plus opt-in territory/zone suggestion chips in the nucleus form (`territorySuggestions`, `NucleusTerritoryAndZonesFields`) — no migration, no forced equality on save. Also the same day: **C2** Cadastro nominal de apoiadores (`supporter` join `Contact`↔campanha with optional `nucleus`, migration `20260718_222656_add_supporter` with `UNIQUE NULLS NOT DISTINCT (contact_id, nucleus_id)`, consent keys `apoiador-cadastro` / `apoiador-intencao-voto` resolved via a generalized `campaignConsent.ts` that fails closed, `/campanha/apoiadores` vertical with list+KPIs/detail/CSV-import wizard; phone required in v1, `lideranca` has no access to the area; production use with real data still waits on Consent keys + legal sign-off). Also the same day: C3 Planos de Ação (`actionPlan` + `/campanha/planos`, migration `20260718_222832_add_action_plan`, blocos "Próximos eventos"). Also the same day: **B2** map foundation — static TopoJSON for Bahia municipalities + identity territories, `bahiaMunicipalityCodes`, helpers in `bahiaGeometries`, re-runnable `pnpm build:geometries` (no migration, no UI; Leaflet is B3). Post-`/simplify` scale follow-ups for that delivery are registered as **B5** (`docs/plans/escala-dry-pos-b2.md`). Earlier (2026-07-15): local Postgres via `docker-compose.yml` (Postgres 17, `teqo_test`); Payload migrations baselined; dev/test database guards; `post`/`tag` news routes deployed (see "Posts & Tags" below).
|
||||
|
||||
**Recently resolved (2026-07-18 evening / 2026-07-19):** **A4** Baseline no produto + Gap vs 2022 shipped and merged to `main` — `getNucleusElectoralBaseline` / `loadNucleusBaseline2022Overview` aggregate TSE 2022 by nucleus geography; `computeGapVs2022` + `NucleusInsights` on the overview tab; `NucleusElectoralBaseline` card (candidate / president / governor via `BASELINE_TICKET_2022`); "Baseline 2022" card on the filtered nuclei-list overview. No migration (reads existing A3 collections). A `/simplify` pass applied targeted cleanup (role-generic ticket config, per-city `zoneNumber in […]`, shared geography helpers). Scale/DRY debits larger than cleanup (aggregate federal ranking on detail instead of loading all nominal rows, filter by TSE `cityCode`, Alert `confirmed` + `Progress` reuse) are registered as **A7** (`docs/plans/escala-dry-pos-a4.md`).
|
||||
|
||||
**Recently resolved (2026-07-19):** **C6** Escala e DRY pós-C2 shipped and merged to `main` — bulk supporter import via `payload.db.drizzle` inside the existing Payload transaction (`supporterImportBulk.ts`, `ON CONFLICT DO NOTHING` on `(contact_id, nucleus_id)`, 500-row chunks), `skipContactPhoneInvariant` opt-in on `Contact.enforceUniqueContactPhone` (fail-closed: only honored inside an active `req.transactionID`), single-SQL KPI aggregate (`supporterListOverviewAggregate.ts`, `COUNT(*) FILTER` mirroring `buildSupporterListWhere` + access constraint), HMAC-SHA256 single-use import token + ephemeral `supporterImportBatch` staging collection (migration `20260719_011015_add_supporter_import_batch`, TTL 10 min, actor-bound, consumed on confirm), and shared shells (`campaignListUrl.ts`, `CampaignListPagination`, `campaignFormFields.ts`, `mapCampaignFormActionError.ts`) replacing the three per-list pagination components. A `/simplify` pass applied targeted cleanup (identity maps, dead branches, `relationshipId`/`canManageCampaignUsers` reuse, native `base64url`, `rowCount ?? 0` bug fix). Scale/DRY debits the simplify reviewers flagged as larger than cleanup (locks in 1 round-trip, `.returning()`, `pg_trgm`, shared `drizzleBulk.ts`, column-name assertion, migrating the remaining `formActions`) are registered as **C8** (`docs/plans/escala-dry-pos-c6.md`). Production use with real supporter data still waits on the Onda 0 legal batch (Consent keys `apoiador-cadastro` / `apoiador-intencao-voto`).
|
||||
**Já no produto (resumo — detalhe e próximos em [`docs/roadmap.md`](docs/roadmap.md)):** núcleos MVP + Ciclo 2 (A1–A4, B1–B2, C1–C3, D1); C2 eng. + C6–C9 escala apoiadores; Trilha E parcial (E1–E3); Onda 0 eng. (Consent provisório); fill-ins Visitados / Reset senha+perfil. Débitos abertos (A5–A8, B3–B5, C4–C5, C10–C11, D2, E4–E7, O0+/VR+/RS+/FD+/FD2) só no roadmap.
|
||||
|
||||
## Posts & Tags (news / publications)
|
||||
|
||||
|
|
@ -110,15 +106,15 @@ The first `/campanha` operational cycle is centered on `electoralNucleus`; a Nú
|
|||
|
||||
## Campaign supporters (C2)
|
||||
|
||||
Nominal supporter registry backing the campaign's base of declared supporters (roadmap item C2; engineering ready and merged to `main` on 2026-07-18, production-blocked on the legal batch). One collection in admin group `Campanha`:
|
||||
Nominal supporter registry backing the campaign's base of declared supporters (roadmap item C2; engineering ready and merged to `main` on 2026-07-18; real PII hold ↔ Onda 0 legal batch). One collection in admin group `Campanha`:
|
||||
|
||||
- **`supporter`** (`src/collections/Supporter.ts`) — join `Contact`↔campaign with **optional `nucleus`** (transversal base; aggregates by territory when linked). Fields: `contact` (required, indexed), `nucleus` (optional, indexed), `voteIntention` (select `certo | tende_a_certo | indeciso | outro`, indexed — **sensitive data**, field access restricted to `geral`/`coordenador`), paired consent fields for registration and vote intention (`consent`/`consentContentHash`/`consentedAt` and `voteIntentionConsent`/`voteIntentionConsentContentHash`/`voteIntentionConsentedAt`), `source` (`import_csv | manual | convite | evento`; v1 writes only the first two), `consentNote`, `notes` (staff-only), `createdBy` (readOnly). Uniqueness: `UNIQUE NULLS NOT DISTINCT (contact_id, nucleus_id)` (Postgres ≥15) via migration `20260718_222656_add_supporter`. A `beforeChange` hook sets `createdBy` and rejects a `contact` that is already a `leadership` of the same `nucleus`.
|
||||
- **Consent by stable key.** `campaignConsent.ts` was generalized to `getConsentByKey` / `requireConsentByKey` (fail-closed). The two keys are `apoiador-cadastro` (declared support) and `apoiador-intencao-voto` (vote intention — highlighted, separate consent). The app fails closed if either key is missing — same pattern as `lideranca-autopreenchimento`.
|
||||
- **Access.** `geral` sees/manages all; `coordenador` sees supporters of assigned nuclei (`nucleus in getAccessibleNucleusIds`); supporters without a nucleus are `geral`-only; `lideranca` has no access to the area. Delete is admin-only. Access helpers live in `campaignAccess.ts` (`canReadSupporter` / `canManageSupporter` / `canCreateSupporter`).
|
||||
- **Server actions** (`src/app/(campaign)/campanha/actions/supporter.ts`): `createSupporter`, `setSupporterVoteIntention`, `previewSupporterImport` / `confirmSupporterImport` (CSV, `geral`-only, transactional with `req: { transactionID }`, dedup by phone via `contactPhoneInvariant`), and `removeSupporterData` (deletes the `supporter`; anonymizes/clears `Contact` PII only if no other join — `leadership`/`signature`/`subscription`/other `supporter` — references it).
|
||||
- **UI** `/campanha/apoiadores` — list with KPIs (total / "Certo + Tende" / "Indecisos"), filters (vote intention, territory), detail with LGPD consent block + vote-intention segmented control (disabled until consent) + minimal share kit (`wa.me` + copy text), and a 3-step CSV import wizard (Upload → Preview → Confirm). Phone is required in v1.
|
||||
- **C6 scale layer (merged to `main` 2026-07-19).** CSV import up to 5000 rows now completes in one transaction: `confirmSupporterImportRecord` acquires all phone advisory locks, then `bulkInsertSupporterImport` (`src/utilities/supporterImportBulk.ts`) inserts contacts + supporters via `payload.db.drizzle` on the Payload transaction session (`getPostgresTransactionDatabase`) in 500-row chunks with `ON CONFLICT DO NOTHING` on `(contact_id, nucleus_id)`. `Contact.enforceUniqueContactPhone` honors `context.skipContactPhoneInvariant` **fail-closed** (only inside an active `req.transactionID`), so the bulk path skips the per-row re-check while the single-create path is unchanged. Preview no longer round-trips the full `ok` set: `previewSupporterImportText` stages it in `supporterImportBatch` (`src/collections/SupporterImportBatch.ts`, admin-hidden, `geral`/admin only, migration `20260719_011015_add_supporter_import_batch`) and returns an HMAC-SHA256 single-use `importToken` (10-min TTL, actor-bound, `src/utilities/supporterImportToken.ts`); `confirmSupporterImportRecord` verifies the token and consumes the batch (delete after commit). The list KPI is a single `COUNT(*) FILTER` aggregate (`src/utilities/supporterListOverviewAggregate.ts`) mirroring `buildSupporterListWhere` + the access constraint, replacing 3× `payload.count`. Shared shells — `campaignListUrl.ts`, `CampaignListPagination`, `campaignFormFields.ts`, `mapCampaignFormActionError.ts` — replace the three per-list pagination components and the duplicated form-error ladders. Scale/DRY follow-ups from the `/simplify` pass are registered as **C8** (`docs/plans/escala-dry-pos-c6.md`).
|
||||
- **Mandatory production blocker:** do not import real supporter data or capture vote intention until electoral counsel documents the LGPD art. 11 basis (vote intention is sensitive data), approves the versioned consent texts, and an admin creates `Consent.key = 'apoiador-cadastro'` and `Consent.key = 'apoiador-intencao-voto'`. The app intentionally fails closed when these keys are absent.
|
||||
- **C6–C9 scale layers (merged to `main` 2026-07-19).** C6 bulk import + KPI + shells; C7 território/leituras; C8 locks/bulk/`pg_trgm`; C9 filtros unificados / loader. Open scale follow-ups: **C10** / **C11** (`docs/plans/escala-dry-pos-c9.md`, `docs/plans/escala-dry-pos-c7.md`).
|
||||
- **Mandatory production blocker (PII real):** provisional `apoiador-cadastro` / `apoiador-intencao-voto` keys are already seeded (Onda 0). Do not import real supporter data or capture vote intention until electoral counsel documents the LGPD art. 11 basis (vote intention is sensitive data) and approves the **final** versioned consent texts that replace the provisórios. The app fails closed if keys are absent; with provisórios present, the hold is legal/operational.
|
||||
|
||||
## Campaign action plans (C3)
|
||||
|
||||
|
|
@ -151,7 +147,7 @@ Public TSE 2022 results back the electoral baseline (roadmap items A3 + A4). Thr
|
|||
3. Run `pnpm generate:types`, `pnpm generate:importmap` when components changed, `pnpm exec tsc --noEmit`, `pnpm lint`, `pnpm test` (unit + int), `pnpm test:e2e` (or `pnpm test:all`), and `pnpm build` against the local database. Payload CLI scripts (`migrate`, `generate:*`, `payload`) and the build migrate step use `--conditions=react-server` so `server-only` modules load correctly outside Next.js.
|
||||
4. Scan every edited runtime source file with Aikido and resolve new findings before handoff. Do not expand hardening work into unrelated pre-existing findings without an explicit scope change.
|
||||
5. Review Vercel environment variables and migration SQL before deploy; `pnpm build` applies pending migrations to production automatically.
|
||||
6. **Mandatory production blocker:** do not insert real leadership/support data or enable invites until electoral counsel documents the LGPD art. 11 basis, approves the specific versioned consent text, and an admin creates it with exactly `Consent.key = 'lideranca-autopreenchimento'`. The app intentionally fails closed when this key is absent.
|
||||
6. **Mandatory production blocker (PII real):** provisional Consent keys + `/privacidade` are auto-provisioned (Onda 0 migrations / `pnpm db:seed:consent`). Do not insert real leadership/supporter PII or enable mass invites/imports until electoral counsel approves the **final** versioned texts (including `lideranca-autopreenchimento`). The app fails closed if keys are absent; with provisórios present, the hold is legal/operational — see [`docs/plans/onda-0.md`](docs/plans/onda-0.md).
|
||||
|
||||
## Core Principles
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ Two surfaces, one product.
|
|||
|
||||
**Public site:** own the relationship with the base first (engagement channel the campaign controls); project the mandate’s official voice and credibility second.
|
||||
|
||||
**Success near-term:** `/campanha` is the daily ops tool with real nuclei, people, and estimates in use, and the public site is the owned citizen channel — both live, not one or the other.
|
||||
**Success near-term:** `/campanha` is the daily ops tool with real nuclei, people, and estimates in use, and the public site is the owned citizen channel — both live, not one or the other. **Operational hold:** real leadership/supporter PII waits on final Onda 0 counsel texts ([`docs/roadmap.md`](docs/roadmap.md)).
|
||||
|
||||
## Positioning
|
||||
|
||||
|
|
|
|||
|
|
@ -16,10 +16,11 @@ The goal is to help political teams build direct, durable relationships with the
|
|||
|
||||
Initial delivery focuses on Jorge Solla's public website, editorial CMS, and the internal `/campanha` tool:
|
||||
|
||||
- Public-facing content and updates (news/`post` + `tag` live)
|
||||
- Institutional pages and biography (still pending a `Pages` collection)
|
||||
- Internal campaign operations: electoral nuclei, leaderships, estimates, updates, WhatsApp invites
|
||||
- Public-facing news (`post` / `tag` live); institutional `Pages` and editable home copy still pending
|
||||
- `/privacidade` live (provisional Onda 0 text — final counsel copy pending)
|
||||
- Internal `/campanha`: nuclei, leaderships, estimates, updates, WhatsApp invites, supporters (eng. ready; real PII ↔ Onda 0), action plans, TSE baseline + goals/trend, PWA
|
||||
- Editorial operations through Payload CMS
|
||||
- Backlog / calendar windows: [`docs/roadmap.md`](docs/roadmap.md)
|
||||
|
||||
### Phase 2: White-Label Platform
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Status: Fases 1–4 implementadas e mescladas em `main` (A3 modelo+import; A4 detalhe + overview + Gap vs 2022 + simplify). Follow-up de escala/DRY: [A7](escala-dry-pos-a4.md).
|
||||
Atualizado em: 2026-07-19
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (A3/A4 Baseline TSE 2022)
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (A3/A4 — Já entregue; débitos A7)
|
||||
Responsável: —
|
||||
|
||||
## Referência visual (UX Pilot)
|
||||
|
|
@ -195,13 +195,13 @@ Cada insight é uma **derivação de leitura** (sem escrita, sem `Consent`, sem
|
|||
- Dados por seção eleitoral (grain escolhido: município+zona).
|
||||
- Outras eleições além de 2022 (extensível via `year`; 2026 entra reusando o mesmo modelo quando o TSE publicar).
|
||||
- Outros estados além da BA (todo núcleo é baiano; `state` fica `BA`).
|
||||
- Previsão estatística de votos (roadmap linha 62) — aqui só baseline histórico + comparação com estimativa manual.
|
||||
- Mapa/PostGIS (roadmap linha 52) — separado.
|
||||
- Previsão estatística de votos (roadmap (Fora de escopo / Próximos)) — aqui só baseline histórico + comparação com estimativa manual.
|
||||
- Mapa/PostGIS (roadmap (Fora de escopo / Próximos)) — separado.
|
||||
- Substitui o plano `zonas-por-municipio.md` — ele continua válido como melhoria de qualidade do input (dependência suave, ver "Dependências").
|
||||
|
||||
## Referências
|
||||
|
||||
- `docs/roadmap.md` (seção "Campanha → Próximos ciclos")
|
||||
- `docs/roadmap.md` (Próximos / Já entregue)
|
||||
- `docs/plans/zonas-por-municipio.md` — dependência suave (`tseZonesForCity` como fonte preferida de zonas por cidade; `citiesForTerritory` já existe em `bahiaTerritories.ts`)
|
||||
- `docs/plans/overview-lista-nucleos.md` — Fase 3 estende este plano
|
||||
- `src/collections/ElectoralNucleus.ts` — `city`, `region`, `tseZones`
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Status: engenharia pronta e mesclada em `main` (pendente deploy + lote jurídico)
|
||||
Atualizado em: 2026-07-18
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (seção "Campanha → Próximos ciclos")
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (C2 — Já entregue eng.; dados reais ↔ Onda 0)
|
||||
Responsável: —
|
||||
|
||||
## Referência visual (UX Pilot)
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ Importante: compartilhar **não concede acesso**. O link aponta para `/campanha/
|
|||
|
||||
- **Escopo: só o detalhe do núcleo.** O botão vive no header de `src/app/(campaign)/campanha/(app)/nucleos/[slug]/page.tsx`. A listagem de núcleos (`NucleusCard`) fica fora deste ciclo (ver "Não escopo").
|
||||
- **Compartilhar não é convite.** Não cria `campaignInvite`, não gera token, não expira, não revoga. Reusa o padrão `wa.me` apenas como canal (mesma decisão do notebook: "Convidar pelo WhatsApp" cria convite; aqui não).
|
||||
- **Sem WhatsApp Business API.** Mesma justificativa do roadmap linha 57: o remetente envia pelo seu próprio WhatsApp, abrindo `wa.me` em nova aba com `noopener,noreferrer` (igual ao `LeadershipInviteDialog`).
|
||||
- **Sem WhatsApp Business API.** Mesma justificativa do roadmap (Fora de escopo / Próximos): o remetente envia pelo seu próprio WhatsApp, abrindo `wa.me` em nova aba com `noopener,noreferrer` (igual ao `LeadershipInviteDialog`).
|
||||
- **Sem server action.** A URL `wa.me` é montada no cliente a partir do telefone do destinatário (já carregado no server) + mensagem com remetente/nome do núcleo/link. Nenhuma escrita, nenhuma transação, nenhum `req`.
|
||||
- **Access control por `overrideAccess: false`.** Os telefones dos destinatários vêm de queries com `user` + `overrideAccess: false`, então o field access existente (`canReadCampaignUserPhone`, `canReadContacts`) filtra naturalmente quem pode ver qual telefone. O plano não inventa regra nova.
|
||||
- **Link compartilhado = URL canônica do núcleo.** `${NEXT_PUBLIC_SITE_URL}/campanha/nucleos/${slug}` — reusa `getCampaignInviteBaseURL()` de `src/utilities/campaignInvite.ts` para derivar a origem (mesmo guard de produção/localhost).
|
||||
|
|
@ -113,13 +113,13 @@ Componentes:
|
|||
|
||||
- Compartilhar a partir da listagem de núcleos (`NucleusCard` actions) — só o detalhe neste ciclo.
|
||||
- Compartilhar outras páginas da campanha (dashboard, lideranças) — o padrão é extensível, mas fica para depois.
|
||||
- WhatsApp Business API (item separado, roadmap linha 57).
|
||||
- WhatsApp Business API (item separado, roadmap (Fora de escopo / Próximos)).
|
||||
- Criar convite/acesso — compartilhar não concede nada; o destinatário já precisa ter acesso.
|
||||
- Compartilhar por canal que não WhatsApp (e-mail, SMS, push) — só `wa.me` + copiar link.
|
||||
|
||||
## Referências
|
||||
|
||||
- `docs/roadmap.md` (linha 51)
|
||||
- `docs/roadmap.md` (Próximos — ver ID do item)
|
||||
- `src/app/(campaign)/campanha/(app)/nucleos/[slug]/page.tsx` — header onde o botão entra
|
||||
- `src/utilities/phone.ts` — `buildWhatsAppUrl` (`wa.me`, normalização 11 dígitos)
|
||||
- `src/utilities/campaignInvite.ts` — `buildCampaignInviteWhatsAppLink` (referência de texto), `getCampaignInviteBaseURL`
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Status: rascunho
|
||||
Atualizado em: 2026-07-17
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (seção "Campanha → Próximos ciclos", linha 55)
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (C4 — Próximos / Janela 3)
|
||||
Responsável: —
|
||||
|
||||
## Contexto
|
||||
|
|
@ -123,7 +123,7 @@ Componentes:
|
|||
|
||||
## Referências
|
||||
|
||||
- `docs/roadmap.md` (linha 55)
|
||||
- `docs/roadmap.md` (Próximos — ver ID do item)
|
||||
- `docs/plans/overview-lista-nucleos.md` — bloco "Demandas" já desenhado
|
||||
- `docs/plans/eventos-agenda-mobilizacao.md` — entidade `actionPlan` (relação opcional)
|
||||
- `src/collections/ElectoralNucleus.ts` — padrão de collection, hooks de slug/território, campos derivados
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Status: implementado e mesclado em `main`
|
||||
Atualizado em: 2026-07-18
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (Janela 2, ordem 9 — C3)
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (C3 — Já entregue)
|
||||
Responsável: —
|
||||
|
||||
## Referência visual (UX Pilot)
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ Componentes:
|
|||
|
||||
## Referências
|
||||
|
||||
- `docs/roadmap.md` — A5 Insights; grafo A4 → A5; Janela 3 ordem 19
|
||||
- `docs/roadmap.md` — A5 Insights; grafo A4 → A5; Janela 3 — A5
|
||||
- [baseline-eleitoral-tse.md](baseline-eleitoral-tse.md) — A3/A4
|
||||
- [escala-dry-pos-a4.md](escala-dry-pos-a4.md) — A7 F1 (agregação federal)
|
||||
- [insight-dobradinha-2026.md](insight-dobradinha-2026.md) — taxonomia futura
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Status: rascunho
|
||||
Atualizado em: 2026-07-17
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (seção "Campanha → Próximos ciclos")
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (A5 — Próximos / Janela 3)
|
||||
Responsável: —
|
||||
|
||||
## Referência visual (UX Pilot)
|
||||
|
|
@ -61,7 +61,7 @@ flowchart LR
|
|||
|
||||
## Não escopo
|
||||
|
||||
- Mapa/PostGIS com a classe pintada por território (roadmap linha 52).
|
||||
- Mapa/PostGIS com a classe pintada por território (roadmap (Fora de escopo / Próximos)).
|
||||
- Pesquisa de intenção/rejeição (domínio inexistente; refinamento futuro).
|
||||
|
||||
## Referências
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Status: rascunho
|
||||
Atualizado em: 2026-07-17
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (seção "Campanha → Próximos ciclos")
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (A6 — Próximos / Janela 4)
|
||||
Responsável: —
|
||||
|
||||
## Contexto
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Status: rascunho
|
||||
Atualizado em: 2026-07-17
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (seção "Campanha → Próximos ciclos")
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (A5 — Próximos / Janela 3)
|
||||
Responsável: —
|
||||
|
||||
## Referência visual (UX Pilot)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Status: rascunho
|
||||
Atualizado em: 2026-07-17
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (seção "Campanha → Próximos ciclos")
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (A5 — Próximos / Janela 3)
|
||||
Responsável: —
|
||||
|
||||
## Referência visual (UX Pilot)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Status: E1, E2 e E3 implementados e mesclados em `main` (2026-07-19); E4 e E5 pendentes
|
||||
Atualizado em: 2026-07-19 (revisão entrega E1+E3 — marcar como entregues no roadmap; access E1/E3 sem `canSetDerivedNucleusField`; escrita via `canUpdateElectoralNucleus` + selects/VMs)
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (Trilha E — itens E1–E5, "Ciclo 3")
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (Trilha E — E1–E3 ✓; E4–E7 abertos)
|
||||
Responsável: —
|
||||
|
||||
## Contexto
|
||||
|
|
@ -104,7 +104,7 @@ flowchart LR
|
|||
|
||||
## Referências
|
||||
|
||||
- `docs/roadmap.md` — Trilha E / Ciclo 3, janelas 2–3
|
||||
- `docs/roadmap.md` — Trilha E — Próximos / janelas 2–3
|
||||
- `docs/sheets/Mapa_projecao_votos_Solla_2026.xlsx` e `docs/sheets/Mapa projeção de votos Solla 2026.xlsx` — planilhas-fonte (estrutura, abas RESUMO/PRIORITÁRIAS/SALVADOR)
|
||||
- `src/collections/ElectoralNucleus.ts` — campos, hooks e access do núcleo (E1/E3)
|
||||
- `src/lib/electionInsights.ts` + `src/utilities/nucleusElectoralBaseline.ts` — padrão de insight derivado e agregação do baseline (E2)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Status: rascunho
|
||||
Atualizado em: 2026-07-17
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (seção "Campanha → Próximos ciclos")
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (D2 — Próximos / Janela 3)
|
||||
Responsável: —
|
||||
|
||||
## Referência visual (UX Pilot)
|
||||
|
|
@ -24,7 +24,7 @@ Coordenadores e lideranças precisam ser avisados de eventos da campanha (convit
|
|||
- **Push web** via Push API + Service Worker + VAPID (entrega em segundo plano, inclusive com o app fechado).
|
||||
- **Sino de notificações in-app** dentro do `/campanha` (central de avisos lidos/não lidos).
|
||||
|
||||
Notificações por WhatsApp Business API ficam **fora** deste plano — já existem como item separado no roadmap (linha 57), exigem Meta + templates e serão reavaliadas quando o volume justificar.
|
||||
Notificações por WhatsApp Business API ficam **fora** deste plano — vedadas para campanhas (Meta + Res. TSE 23.610 art. 33; roadmap Fora de escopo); push + sino cobrem a necessidade.
|
||||
|
||||
## Objetivos
|
||||
|
||||
|
|
@ -84,6 +84,6 @@ Componentes:
|
|||
|
||||
## Referências
|
||||
|
||||
- `docs/roadmap.md` (linhas 57, 60, 61)
|
||||
- `docs/roadmap.md` (Próximos — ver ID do item)
|
||||
- `AGENTS.md` — Campaign auth, Campaign nuclei MVP, transações, `Consent` por chave
|
||||
- `.cursor/rules/projects/nucleos-eleitorais.mdc`
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Status: implementado (engenharia)
|
||||
Atualizado em: 2026-07-19
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (Onda 0 — Caminho crítico para `/campanha` em produção)
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (Onda 0 — caminho crítico para dados reais)
|
||||
Responsável: —
|
||||
|
||||
## Contexto
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Status: **implementado** (2026-07-18)
|
||||
Atualizado em: 2026-07-18
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (Trilha B → B1)
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (B1 — Já entregue)
|
||||
Responsável: —
|
||||
|
||||
## Como foi implementado (2026-07-18)
|
||||
|
|
@ -47,7 +47,7 @@ A decisão de produto (2026-07-17) é adicionar um **painel de overview acima da
|
|||
|
||||
## Decisões travadas
|
||||
|
||||
- **Mesmos filtros da página.** O overview é computado a partir do mesmo `NucleusListState` já parseado em `loadNucleusListPageData` (`parseNucleusListParams` + `buildNucleusListWhere`). Não existe filtro "do overview" separado do filtro "da lista". (Decisão de produto 2026-07-17; roadmap linha 50.)
|
||||
- **Mesmos filtros da página.** O overview é computado a partir do mesmo `NucleusListState` já parseado em `loadNucleusListPageData` (`parseNucleusListParams` + `buildNucleusListWhere`). Não existe filtro "do overview" separado do filtro "da lista". (Decisão de produto 2026-07-17; roadmap (Fora de escopo / Próximos).)
|
||||
- **Agregados sobre o conjunto inteiro filtrado, não só a página atual.** A lista é paginada em 25, mas o overview precisa de totais — logo exige uma query adicional com `pagination: false` selecionando só os campos de agregação. Mesmo padrão já usado por `getCampaignDashboardPageData` (que carrega todos os núcleos ativos com `pagination: false`).
|
||||
- **Sem collection/migration/server action.** Tudo é leitura no server component. Nenhuma escrita, nenhuma transação, nenhum `Consent`.
|
||||
- **Access control por `overrideAccess: false`.** O overview herda naturalmente o escopo de papel: `geral` vê tudo, `coordenador` vê só os seus núcleos, `lideranca` vê só os núcleos com liderança engajada — exatamente como a lista já faz. Para "Últimas atualizações", `canReadNucleusUpdate` já restringe `lideranca` ao próprio autor; o preview de `lideranca` mostra só os próprios reportes nos núcleos filtrados. O plano não inventa regra nova.
|
||||
|
|
@ -122,13 +122,13 @@ Componentes:
|
|||
- Criar as collections Eventos ou Demandas — domínios separados no roadmap.
|
||||
- Alterar os filtros existentes ou a paginação da lista — o overview é aditivo e reage ao estado já existente.
|
||||
- Substituir o dashboard `/campanha` — o dashboard continua o recorte global (todos os ativos, sem filtros de lista); o overview é o recorte filtrado da lista.
|
||||
- Estimativa estatística/previsão de votos (roadmap linha 60) — aqui só somamos `confirmedVoteEstimate`.
|
||||
- Estimativa estatística/previsão de votos (roadmap (Fora de escopo / Próximos)) — aqui só somamos `confirmedVoteEstimate`.
|
||||
- Novo access control ou novo `Consent` — herda o que existe.
|
||||
- Compartilhar núcleo a partir da lista (`NucleusCard` actions) — escopo do plano `compartilhar-pagina.md` (só o detalhe neste ciclo).
|
||||
|
||||
## Referências
|
||||
|
||||
- `docs/roadmap.md` (linha 50, e linhas 54–55 para Eventos/Demandas)
|
||||
- `docs/roadmap.md` (Próximos — ver ID do item)
|
||||
- `src/app/(campaign)/campanha/(app)/nucleos/page.tsx` — página onde o overview entra
|
||||
- `src/utilities/nucleusPageData.ts` — `loadNucleusListPageData` (mesma `state`)
|
||||
- `src/utilities/nucleusUi.ts` — `NucleusListState`, `parseNucleusListParams`, `buildNucleusListWhere`
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Status: **implementado** (2026-07-18)
|
||||
Atualizado em: 2026-07-18
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (Trilha D → D1)
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (D1 — Já entregue)
|
||||
Responsável: —
|
||||
|
||||
## Como foi implementado (2026-07-18)
|
||||
|
|
@ -46,7 +46,7 @@ A decisão de produto (2026-07-17) é deliberadamente restritiva: **só `/campan
|
|||
|
||||
## Decisões travadas
|
||||
|
||||
- **Escopo restrito a `/campanha`.** `scope` e `start_url` apontam para `/campanha/`; o service worker tem `scope: '/campanha/'`. Site público e `/admin` não são instaláveis e não têm SW. (Decisão de produto 2026-07-17; roadmap linha 81.)
|
||||
- **Escopo restrito a `/campanha`.** `scope` e `start_url` apontam para `/campanha/`; o service worker tem `scope: '/campanha/'`. Site público e `/admin` não são instaláveis e não têm SW. (Decisão de produto 2026-07-17; roadmap (Fora de escopo / Próximos).)
|
||||
- **Um único service worker para a vertical**, compartilhado com o push de notificações — não criar um segundo SW só para push.
|
||||
- **Auth continua via cookie `campaign-token`** (httpOnly, `path: '/campanha'`). O SW roda same-origin e envia cookies automaticamente nos `fetch`; nenhum token extra é exposto ao SW.
|
||||
- **Offline é best-effort, não garantido.** O app continua server-renderizado com auth; o SW faz cache de runtime (stale-while-revalidate) das respostas já vistas e um precache mínimo do shell. Não há sincronização em segundo plano de escritas neste ciclo (write-back offline fica fora de escopo).
|
||||
|
|
@ -144,14 +144,14 @@ Recomendação de instalação do `/campanha` como PWA, exibida no início da se
|
|||
|
||||
## Não escopo
|
||||
|
||||
- PWA do site público `(frontend)` ou do `/admin` (decisão de produto; roadmap linha 81).
|
||||
- PWA do site público `(frontend)` ou do `/admin` (decisão de produto; roadmap (Fora de escopo / Próximos)).
|
||||
- Write-back offline de atualizações/estimativas — escritas continuam exigindo rede e transação Payload.
|
||||
- Push web em si — fica em [plans/notifications.md](./notifications.md); aqui só o placeholder do handler.
|
||||
- Sincronização em segundo plano (Background Sync API).
|
||||
|
||||
## Referências
|
||||
|
||||
- `docs/roadmap.md` (linhas 58, 62, 81)
|
||||
- `docs/roadmap.md` (Próximos — ver ID do item)
|
||||
- `docs/plans/notifications.md` — push depende deste plano
|
||||
- `AGENTS.md` — Campaign auth, naming conventions, transações
|
||||
- `.cursor/rules/projects/nucleos-eleitorais.mdc`
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Status: implementado
|
||||
Atualizado em: 2026-07-19
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (Fill-ins — UX adiada do ciclo 1)
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (Fill-ins — Reset senha/perfil ✓; RS+ aberto)
|
||||
Responsável: —
|
||||
|
||||
## Contexto
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Status: **implementado** (2026-07-18)
|
||||
Atualizado em: 2026-07-18
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (Trilha A → A1)
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (A1 — Já entregue)
|
||||
Responsável: —
|
||||
|
||||
## Referência visual (UX Pilot)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Status: implementado
|
||||
Atualizado em: 2026-07-19
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (seção "Campanha → Próximos ciclos")
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (Fill-ins — Visitados ✓; VR+ aberto)
|
||||
Responsável: —
|
||||
|
||||
## Contexto
|
||||
|
|
@ -29,7 +29,7 @@ A `window.history` **não expõe** a lista de entradas (URLs) da stack por motiv
|
|||
|
||||
## Decisões travadas
|
||||
|
||||
- **Sem modelo no servidor.** Nenhuma collection, nenhum campo em `campaignUser`, nenhuma migration, nenhuma server action de escrita, nenhum `Consent`. Todo o estado vive em `localStorage` no dispositivo do usuário. (Decisão de produto 2026-07-17; roadmap "Campanha → Próximos ciclos".)
|
||||
- **Sem modelo no servidor.** Nenhuma collection, nenhum campo em `campaignUser`, nenhuma migration, nenhuma server action de escrita, nenhum `Consent`. Todo o estado vive em `localStorage` no dispositivo do usuário. (Decisão de produto 2026-07-17; roadmap (Fill-ins / Próximos).)
|
||||
- **`localStorage`, não `sessionStorage`.** A intenção é "voltar aonde eu estava" entre sessões; `sessionStorage` morre ao fechar a aba. `localStorage` persiste por origem+dispositivo até limpeza. Chave estável: `teqo:campaign:recent-visits`.
|
||||
- **Por dispositivo, sem sync.** O histórico é local ao navegador. Não há sincronização entre dispositivos do mesmo usuário (isso exigiria modelo no servidor — fora de escopo). Aceito como trade-off do "sem modelo".
|
||||
- **Registro client-side derivado da navegação real.** Como `window.history` não enumera entradas, um componente cliente invisível (`RecentVisitTracker`) registra cada visita "interessante" no `localStorage`. A entrada é produzida no servidor (label) e gravada no cliente (storage) — sem round-trip de escrita.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Status: entregue
|
||||
Atualizado em: 2026-07-18
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (Trilha A → A2)
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (A2 — Já entregue)
|
||||
Responsável: —
|
||||
|
||||
Revisões:
|
||||
|
|
@ -138,7 +138,7 @@ Componentes (as-built):
|
|||
|
||||
## Referências
|
||||
|
||||
- `docs/roadmap.md` (Trilha A → A2; Janela 1 ordem 3)
|
||||
- `docs/roadmap.md` (A2 — Já entregue)
|
||||
- `src/components/campaign/NucleusForm.tsx`, `NucleusTerritoryAndZonesFields.tsx`, `TerritorySuggestionChips.tsx`, `TseZoneInput.tsx`
|
||||
- `src/lib/bahiaTseZones.ts`, `src/lib/territorySuggestions.ts`
|
||||
- `src/lib/bahiaTerritories.ts`, `tests/fixtures/bahia-identity-territories.official.json`, `tests/int/bahiaTerritories.int.spec.ts` — padrão espelhado
|
||||
|
|
|
|||
465
docs/roadmap.md
465
docs/roadmap.md
|
|
@ -1,383 +1,270 @@
|
|||
# Roadmap — Teqo
|
||||
|
||||
Atualizado em: 2026-07-19 (MVP + Ciclo 2 deployados; A2 entregue; A4 Baseline no produto + Gap vs 2022 implementado e mesclado em `main` (Fases 2–4 + simplify); A7 registrado — escala/DRY pós-A4 do `/simplify`; C2 engenharia pronta e mesclada em `main`; C3 Planos de Ação implementado e mesclado em `main`; C6 Escala e DRY pós-C2 implementado e mesclado em `main` (Fases 1–5 + simplify); C7 Escala e DRY pós-C3 implementado (Fases 1–5; feed O(n) condicional fora de escopo); C8 Escala e DRY pós-C6 implementado e mesclado em `main` (Fases 1–4 + duas passagens `/simplify`); C9 Escala e DRY pós-C8 implementado e mesclado em `main` (Fases 1–4 + simplify); C10 registrado — escala/DRY pós-C9 do `/simplify`; **C11 registrado** — escala/DRY pós-C7 do `/simplify` — [plano](plans/escala-dry-pos-c7.md); B2 fundação de geometrias do mapa entregue; B5 registrado — escala/DRY pós-B2 do `/simplify`; **Trilha E registrada — E1–E5 mapa de projeção por município**, equiparar/superar as planilhas "Mapa de projeção de votos 2026" — [plano](plans/mapa-projecao-municipios.md); **E1 metas em cenários + prioridade e E3 dobradinhas/encaminhamentos manuais implementados e mesclados em `main` 2026-07-19** (migration `20260719_054522_add_nucleus_goals_strategy`; `/simplify` → E6); **E2 série TSE 2014/2018 + tendência implementado 2026-07-19**; **E7 registrado** — escala/DRY pós-E2 do `/simplify` — [plano](plans/escala-dry-pos-e2.md); **E6 registrado** — escala/DRY pós-E1+E3 do `/simplify` (aggregate lista/overview, geografia baseline, DRY formatação) — [plano](plans/escala-dry-pos-e1.md); **Onda 0 MVP** — Consent/privacidade provisórios auto-provisionados (migrations) + `/privacidade`; hold de PII real até jurídico — [plano](plans/onda-0.md); **O0+ registrado** — escala/DRY pós-Onda 0 do `/simplify` — [plano](plans/escala-dry-pos-onda0.md); **Visitados recentemente MVP** implementado (fill-in, client-side `localStorage`) — [plano](plans/visitados-recentemente.md); **VR+ registrado** — escala/DRY pós-visitados recentemente do `/simplify` — [plano](plans/escala-dry-pos-visitados-recentemente.md); **Reset senha + foto de perfil MVP** implementado (fill-in, Resend + `/campanha/perfil`) — [plano](plans/reset-senha-foto-perfil.md); **RS+ registrado** — escala/DRY pós-reset senha/perfil do `/simplify` — [plano](plans/escala-dry-pos-reset-senha-perfil.md); **A5 alavancagem da chapa expandido** — Fase 2 “oportunidade de virada” (majoritários esquerda + federal direita por vencedor **ou** por participação proporcional alta) absorvida em [insight-alavancagem-chapa.md](plans/insight-alavancagem-chapa.md), sem item paralelo; **A8 registrado** — perfil médio do eleitorado via IBGE (Censo) + manuais na aba Eleitorado — [plano](plans/perfil-eleitorado-ibge.md); **Field Desk polish** (Impeccable quieter→polish, critique 28→32) em branch local — [PRODUCT.md](../PRODUCT.md) / [DESIGN.md](../DESIGN.md); **FD+ registrado** — escala/DRY pós-Field Desk do `/simplify` — [plano](plans/escala-dry-pos-field-desk.md); **FD2 registrado** — UX pós-critique (bulk fila, glossário, outline, empty coordenador) — [plano](plans/field-desk-ux-pos-critique.md))
|
||||
Atualizado em: 2026-07-19 (janela 1 vigente; foco: Onda 0 + base operacional; último compile)
|
||||
|
||||
Registro canônico no repositório dos planos futuros e débitos conhecidos. Status operacional do ciclo atual de Núcleos fica em [`.cursor/rules/projects/nucleos-eleitorais.mdc`](../.cursor/rules/projects/nucleos-eleitorais.mdc); este arquivo lista o que ainda é futuro ou bloqueador, **em ordem de execução**, com dependências e paralelismo explícitos.
|
||||
Registro canônico dos **próximos** planos e débitos. Histórico de entregas: resumo abaixo + planos em [`docs/plans/`](plans/) + notebook [`.cursor/rules/projects/nucleos-eleitorais.mdc`](../.cursor/rules/projects/nucleos-eleitorais.mdc).
|
||||
|
||||
## Âncoras do calendário eleitoral 2026 (Res. TSE 23.760/2026)
|
||||
|
||||
| Data | Marco | Consequência para o produto |
|
||||
| ----------- | ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 20/07–05/08 | Convenções partidárias | Estrutura de núcleos/coordenadores deve estar operando **agora** |
|
||||
| 15/08 | Prazo final de registro de candidaturas | TSE publica candidaturas 2026 na sequência → destrava o insight de dobradinha |
|
||||
| 16/08 | Início da propaganda eleitoral (rua e internet) | "Quem chega em 16/08 com base de dados estruturada tem vantagem operacional" (Politipédia) — base nominal, baseline e agenda precisam estar em produção **antes** desta data |
|
||||
| 09–23/10 | Propaganda gratuita rádio/TV | Reta final; congelar mudanças arriscadas no app |
|
||||
| 04/10 | 1º turno | Operação do dia D (GOTV) — confirmação de comparecimento da base nominal |
|
||||
| 25/10 | Eventual 2º turno | Chapa majoritária (Lula/Jerônimo); Solla decidido no 1º turno |
|
||||
| Data | Marco | Consequência para o produto |
|
||||
| ----------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------- |
|
||||
| 20/07–05/08 | Convenções partidárias | Estrutura de núcleos/coordenadores deve estar operando **agora** |
|
||||
| 15/08 | Prazo final de registro de candidaturas | TSE publica candidaturas 2026 → destrava o insight de dobradinha (A6) |
|
||||
| 16/08 | Início da propaganda eleitoral (rua e internet) | Base nominal, baseline e agenda precisam estar em produção **antes** desta data |
|
||||
| 09–23/10 | Propaganda gratuita rádio/TV | Reta final; congelar mudanças arriscadas no app |
|
||||
| 04/10 | 1º turno | Operação do dia D (GOTV) — confirmação de comparecimento da base nominal |
|
||||
| 25/10 | Eventual 2º turno | Chapa majoritária (Lula/Jerônimo); Solla decidido no 1º turno |
|
||||
|
||||
## Princípios e decisões travadas
|
||||
|
||||
- Priorizar ownership de audiência e portabilidade de dados. _(README)_
|
||||
- Manter módulos genéricos o bastante para reuso em outros contextos políticos. _(README)_
|
||||
- Default seguro e com controle de acesso para equipes de campanha e institucionais. _(README)_
|
||||
- Um único app Next.js com três áreas: site público `(frontend)`, admin Payload `(payload)`, ferramenta interna `(campaign)` em `/campanha`. Sem serviço Rust separado. _(AGENTS.md)_
|
||||
- Hospedagem na Vercel por enquanto; sem migração self-host/Coolify em andamento. _(AGENTS.md)_
|
||||
- Doações **não** entram neste app — só CTA/link para QueroApoiar (`apoiar.me/jorgesolla`, homologado TSE). _(AGENTS.md)_
|
||||
- Pessoa = `Contact` + collections de junção; nunca criar cadastro paralelo de "apoiador/pessoa". _(AGENTS.md)_
|
||||
- Núcleo Eleitoral é unidade operacional da campanha; Zona TSE é referência oficial distinta. _(notebook Núcleos)_
|
||||
- Sem disparo em massa por WhatsApp: vedado pela Res. TSE 23.610 (art. 33) e pela política da Meta; mobilização é orgânica (kits de compartilhamento individuais). _(plano cadastro-nominal, pesquisa 2026-07-17)_
|
||||
- Ownership de audiência e portabilidade de dados; módulos reutilizáveis; default seguro com access control. _(README)_
|
||||
- Um único app Next.js: site `(frontend)`, admin `(payload)`, `/campanha` `(campaign)`. Sem serviço Rust separado. _(AGENTS.md)_
|
||||
- Hospedagem Vercel; doações só CTA → QueroApoiar. _(AGENTS.md)_
|
||||
- Pessoa = `Contact` + collections de junção. Núcleo ≠ Zona TSE. _(AGENTS.md / notebook)_
|
||||
- Sem disparo em massa por WhatsApp (Res. TSE 23.610 art. 33 + política Meta); mobilização orgânica. _(cadastro-nominal)_
|
||||
|
||||
## Onda 0 — Caminho crítico para `/campanha` em produção
|
||||
## Onda 0 — caminho crítico para dados reais
|
||||
|
||||
O MVP de Núcleos está **entregue e deployado** (ondas 1–8 + refactors; 2026-07-18), junto com A1 (território multi-município/bairro), A2 (zonas TSE + sugestões cruzadas — sem migration), A3 (baseline TSE 2022 — modelo + import), A4 (baseline no produto + Gap vs 2022 — mesclado em `main`), B1 (overview da lista), C1 (compartilhar página), C3 (planos de ação / agenda) e D1 (PWA). C2 (cadastro nominal de apoiadores) tem a engenharia pronta e mesclada em `main`, mas segue bloqueada em produção pelo lote jurídico (Consent keys). As migrations (`20260718_010733_consolidate_campaign_schema`, `20260718_190559_territorio_multi_municipio_bairro`, `20260718_195854_add_election_results`, `20260718_222656_add_supporter`, `20260718_222832_add_action_plan`, `20260719_011015_add_supporter_import_batch`) aplicam automaticamente no `pnpm build` da Vercel. O que ainda separa a vertical de uso operacional com dados reais é jurídico + smoke pós-deploy:
|
||||
Engenharia de núcleos / C2 / C3 / baseline / Trilha E (E1–E3) / PWA já está em `main`. Textos provisórios de Consent + `/privacidade` já provisionados ([onda-0.md](plans/onda-0.md)). O que ainda falta para PII real:
|
||||
|
||||
1. **Lote jurídico único de LGPD/Consent** _(externo — assessoria jurídica eleitoral; textos finais substituem os provisórios do [Onda 0 MVP](plans/onda-0.md); PII real de lideranças/apoiadores permanece em hold até lá)_. Uma única rodada cobrindo:
|
||||
- Base do art. 11 da LGPD + texto versionado de `Consent.key = 'lideranca-autopreenchimento'` (bloqueador do MVP de Núcleos; o app falha fechado sem a chave).
|
||||
- Textos de `apoiador-cadastro` e `apoiador-intencao-voto` (bloqueadores do cadastro nominal — [detalhes](plans/cadastro-nominal-apoiadores.md)).
|
||||
- Texto de `campanha-notificacoes-push` (opt-in de push — [detalhes](plans/notifications.md)).
|
||||
- **Aviso de Privacidade / política de privacidade institucional** (obrigação do controlador antes de coleta em massa; também é item do site público).
|
||||
- Avaliação de necessidade de RIPD (tratamento em larga escala + dado sensível).
|
||||
- Racional: fatiar em rodadas separadas multiplica o lead time externo; quatro textos + aviso numa rodada só.
|
||||
2. **Smoke pós-deploy** _(após o build Vercel aplicar a migration)_: conferir `NEXT_PUBLIC_SITE_URL` HTTPS exato, login `/campanha`, criar núcleo de teste, e só então cadastrar o Consent de liderança. Checklist completo no AGENTS.md.
|
||||
3. **Ativação com dados reais assim que (1) liberar a chave de liderança.** (A decisão de 2026-07-17 de não esperar a migração multi-município ficou superada: A1 foi entregue e deployado antes da ativação — coordenadores já estruturam núcleos reais durante as convenções, 20/07–05/08, com o território definitivo.)
|
||||
4. **Onboarding do time real** (usuários `geral`/`coordenador`, primeiros núcleos, treinamento básico de campo).
|
||||
1. **Lote jurídico final** _(assessoria eleitoral)_ — substituir provisórios: `lideranca-autopreenchimento`, `apoiador-cadastro`, `apoiador-intencao-voto`, `campanha-notificacoes-push` + Aviso de Privacidade + avaliação RIPD. Hold de titulares reais até lá.
|
||||
2. **Smoke pós-deploy** — `NEXT_PUBLIC_SITE_URL` HTTPS, login `/campanha`, núcleo de teste; checklist no AGENTS.md.
|
||||
3. **Ativação com dados reais** assim que (1) liberar.
|
||||
4. **Onboarding do time** (`geral`/`coordenador`, primeiros núcleos, treinamento de campo).
|
||||
|
||||
**O0+ Escala e DRY pós-Onda 0 registrado (2026-07-19)** — débitos do `/simplify` que não entraram no cleanup: revalidate de globals pós-migration/seed, módulo único de chaves `Consent`, testes do caminho SQL, DRY Lexical/layout no site público (adiar até `Pages`), link condicional no Footer. Não bloqueia lote jurídico nem smoke com dados fictícios. [Plano](plans/escala-dry-pos-onda0.md).
|
||||
**O0+** (escala/DRY pós-Onda 0) não bloqueia jurídico nem smoke — [plano](plans/escala-dry-pos-onda0.md).
|
||||
|
||||
## Campanha (`/campanha`)
|
||||
## Já entregue (resumo)
|
||||
|
||||
### Ciclo 1 — Núcleos (entregue)
|
||||
- **Núcleos MVP + Ciclo 2** — auth/`campaignUser`, território A1/A2, baseline A3/A4, overview B1, share C1, PWA D1, geometrias B2.
|
||||
- **Operação** — C2 (eng. pronta; prod. ↔ Onda 0), C3 agenda, C6–C9 escala/DRY de apoiadores e planos.
|
||||
- **Trilha E (parcial)** — E1 metas/prioridade + E3 dobradinhas/encaminhamentos manuais; E2 série TSE 2014/2018 + tendência. Faltam E4–E7.
|
||||
- **Fill-ins** — Visitados recentemente; Reset senha + foto de perfil; Onda 0 eng. (Consent provisório + `/privacidade`).
|
||||
|
||||
MVP de território + reporte implementado e enviado (ondas 1–8 + refactors de composição/infra/fixtures): auth isolada `campaignUser` (`geral` / `coordenador` / `lideranca`), núcleos com slug canônico, designação de coordenador, lideranças, estimativas sugerir/confirmar com versão UUID, atualizações semanais, convites WhatsApp (`wa.me`, hash, uso único), dashboard e hardening. Detalhes: [`.cursor/rules/projects/nucleos-eleitorais.mdc`](../.cursor/rules/projects/nucleos-eleitorais.mdc).
|
||||
## Próximos — Campanha (`/campanha`)
|
||||
|
||||
### Ciclo 2 — entregues em 2026-07-18 (mesclados e deployados)
|
||||
### Por trilha (só abertos)
|
||||
|
||||
- **A1 Território multi-município/bairro** — `regions`/`cities`/`neighborhoods` viram arrays `hasMany` com backfill (`20260718_190559_territorio_multi_municipio_bairro`); bairros exigem município único; regiões derivadas no servidor. [Plano](plans/territorio-multi-municipio-bairro.md).
|
||||
- **A3 Baseline TSE 2022 — Fase 1** — collections `electionTally`/`electionCandidateVote`/`electionCandidate` (grupo admin `Dados Eleitorais`), migration `20260718_195854_add_election_results`, import local `pnpm db:seed:tse` idempotente por escopo (year, office, turn). Sem UI ainda — a UI é A4. [Plano](plans/baseline-eleitoral-tse.md).
|
||||
- **B1 Overview da lista de núcleos** — painel agregado (estimativa de votos, cobertura, últimas atualizações) entre filtros e lista, reagindo aos mesmos filtros da URL, com escopo por papel. [Plano](plans/overview-lista-nucleos.md).
|
||||
- **C1 Compartilhar página** — diálogo no detalhe do núcleo com destinatários WhatsApp (coordenação geral, coordenadores, lideranças engajadas) + copiar link; envia só o link, não concede acesso. [Plano](plans/compartilhar-pagina.md).
|
||||
- **D1 PWA `/campanha`** — manifest + service worker escopados em `/campanha`, shell offline, toast de instalação (Android/iOS) e limpeza de cache no logout; fundação do push (D2). [Plano](plans/pwa-campanha.md).
|
||||
- **A** — [A5](plans/insight-taxa-conversao.md) Insights (conversão / classificação / [alavancagem+virada](plans/insight-alavancagem-chapa.md) / [mobilização](plans/insight-mobilizacao-brancos-nulos.md) / [competitiva](plans/insight-inteligencia-competitiva.md)); [A6](plans/insight-dobradinha-2026.md) Dobradinha 2026 (após TSE candidaturas); [A7](plans/escala-dry-pos-a4.md) Escala/DRY pós-A4; [A8](plans/perfil-eleitorado-ibge.md) Perfis eleitorado IBGE.
|
||||
- **B** — B3 Mapa Leaflet nas superfícies ([mapa](plans/mapa-bahia-geometrias.md)); B4 Camada de zonas TSE no mapa; [B5](plans/escala-dry-pos-b2.md) Escala/DRY pós-B2 (lazy geometrias / cache CLI).
|
||||
- **C** — [C4](plans/demandas-campanha.md) Demandas; **C5** Operação dia D / GOTV _(proposto — sem plano; design [`Dia-D-GOTV`](design-refs/latest/Dia-D-GOTV.png))_; [C10](plans/escala-dry-pos-c9.md) Escala/DRY pós-C9; [C11](plans/escala-dry-pos-c7.md) Escala/DRY pós-C7.
|
||||
- **D** — [D2](plans/notifications.md) Notificações (push + sino; push ↔ chave jurídica).
|
||||
- **E** — [E4](plans/mapa-projecao-municipios.md) Import único da planilha; E5 Salvador por bairro _(futuro)_; [E6](plans/escala-dry-pos-e1.md) Escala/DRY pós-E1+E3; [E7](plans/escala-dry-pos-e2.md) Escala/DRY pós-E2.
|
||||
- **Débitos / fill-ins abertos** — [O0+](plans/escala-dry-pos-onda0.md); [VR+](plans/escala-dry-pos-visitados-recentemente.md); [RS+](plans/escala-dry-pos-reset-senha-perfil.md); Field Desk polish _(em branch — PRODUCT.md / DESIGN.md)_; [FD+](plans/escala-dry-pos-field-desk.md); [FD2](plans/field-desk-ux-pos-critique.md); listas globais _(sem plano)_; higiene PascalCase _(notebook)_.
|
||||
|
||||
### Ciclo 2+ — A2 entregue e mesclado em `main` (2026-07-18)
|
||||
### Referências de design (só itens abertos com ref)
|
||||
|
||||
- **A2 Zonas TSE + sugestões cruzadas** — cadastro estático `bahiaTseZones` (TSE 2024 `detalhe_votacao_munzona` BA, 417 municípios), motor puro `territorySuggestions` (inclui `outsideZones`), coordenador `NucleusTerritoryAndZonesFields` com chips `{rótulo} +` opt-in (município/TI → ZEs; irmãos do TI e cidades da ZE → Municípios), `TseZoneInput` controlado. Sem migration, sem igualdade forçada no save. [Plano](plans/zonas-por-municipio.md).
|
||||
Os designs UX Pilot estão em [`docs/design-refs/latest/`](design-refs/latest/). **UX/estrutura é referência; a paleta não** — usar tokens `data-theme='campaign'`.
|
||||
|
||||
### Ciclo 2+ — A4 implementado e mesclado em `main` (2026-07-18)
|
||||
| Item do roadmap | Design | Plano |
|
||||
| --------------- | ------ | ----- |
|
||||
| A5 Insights (5) | `Baseline-Eleitoral-2022` (card Insights) | [conversão](plans/insight-taxa-conversao.md) · [classificação](plans/insight-classificacao-territorial.md) · [alavancagem](plans/insight-alavancagem-chapa.md) · [mobilização](plans/insight-mobilizacao-brancos-nulos.md) · [competitiva](plans/insight-inteligencia-competitiva.md) |
|
||||
| A7 Escala/DRY pós-A4 | — | [escala-dry-pos-a4.md](plans/escala-dry-pos-a4.md) |
|
||||
| A8 Perfis eleitorado IBGE | — | [perfil-eleitorado-ibge.md](plans/perfil-eleitorado-ibge.md) |
|
||||
| B3/B4/B5 Mapa | — (encomendar ou desenhar na implementação) | [mapa-bahia-geometrias.md](plans/mapa-bahia-geometrias.md) · [B5](plans/escala-dry-pos-b2.md) |
|
||||
| C4 Demandas | — | [demandas-campanha.md](plans/demandas-campanha.md) |
|
||||
| C5 Dia D / GOTV | `Dia-D-GOTV` | sem plano ainda |
|
||||
| C10 / C11 Escala | — | [C10](plans/escala-dry-pos-c9.md) · [C11](plans/escala-dry-pos-c7.md) |
|
||||
| D2 Notificações | `Notificacoes-PWA` (central) | [notifications.md](plans/notifications.md) |
|
||||
| E4–E7 Mapa de projeção | seguir `Baseline-Eleitoral-2022` / `Lista-Nucleos-Overview` | [mapa-projecao-municipios.md](plans/mapa-projecao-municipios.md) · [E6](plans/escala-dry-pos-e1.md) · [E7](plans/escala-dry-pos-e2.md) |
|
||||
| O0+ / VR+ / RS+ / FD+ / FD2 | — | [O0+](plans/escala-dry-pos-onda0.md) · [VR+](plans/escala-dry-pos-visitados-recentemente.md) · [RS+](plans/escala-dry-pos-reset-senha-perfil.md) · [FD+](plans/escala-dry-pos-field-desk.md) · [FD2](plans/field-desk-ux-pos-critique.md) |
|
||||
|
||||
- **A4 Baseline no produto + Gap vs 2022** — agregação `getNucleusElectoralBaseline` / `loadNucleusBaseline2022Overview` + `computeGapVs2022`; bloco "Baseline eleitoral 2022" e insight Gap na aba Visão geral do núcleo; card "Baseline 2022" no overview da lista filtrada. Chapa tipada via `BASELINE_TICKET_2022` (papéis `candidate` / `president` / `governor`). Sem migration, sem Consent (dado público TSE). Três passagens `/simplify` no PR; débitos maiores → A7. [Plano](plans/baseline-eleitoral-tse.md).
|
||||
- **A7 Escala e DRY pós-A4 registrado (2026-07-19)** — débitos do `/simplify` que não entraram no cleanup: agregar ranking federal do detalhe (não puxar todas as rows nominais), filtro geográfico por `cityCode` TSE, variant Alert `confirmed` + `Progress` no card. [Plano](plans/escala-dry-pos-a4.md).
|
||||
- **A8 Perfis do eleitorado IBGE registrado (2026-07-19)** — perfil médio/comum do território derivado de indicadores municipais do Censo IBGE (artefato estático + ponderação por `cities[]`), exibido na aba Eleitorado junto com `voterProfiles` manuais já existentes; sem migration, sem Consent. [Plano](plans/perfil-eleitorado-ibge.md).
|
||||
**Sem design nesta leva:** A6, B3/B4, C4 detalhe, E4–E5, fill-ins (listas globais).
|
||||
|
||||
### Ciclo 2+ — B2 fundação de geometrias do mapa (2026-07-18)
|
||||
### Grafo de dependências (abertos + predecessores mínimos)
|
||||
|
||||
- **B2 Mapa Fase 1 (geometrias)** — TopoJSON estático de municípios IBGE (`bahia-municipalities.topo.json`) + Territórios de Identidade por dissolução (`bahia-identity-territories.topo.json`), tabela `bahiaMunicipalityCodes` (nome canônico → `codarea`), helpers `bahiaGeometries` (`getMunicipalityFeature` / `getTerritoryFeature`), script re-executável `pnpm build:geometries`. Sem migration, sem UI (Leaflet = B3). [Plano](plans/mapa-bahia-geometrias.md).
|
||||
- **B5 Escala e DRY pós-B2 registrado (2026-07-18)** — débitos do `/simplify` que não entraram no cleanup: lazy load de geometrias (split mun/TI + dynamic import, preferencialmente com B3) e helper compartilhado de cache CLI (`ensureCachedDownload` para `build:geometries` / `db:seed:tse`). [Plano](plans/escala-dry-pos-b2.md).
|
||||
|
||||
### Ciclo 2+ — C2 e C3 mesclados em `main` (2026-07-18)
|
||||
|
||||
- **C2 Cadastro nominal de apoiadores** — collection `supporter` (join `Contact`↔campanha, núcleo opcional), migration `20260718_222656_add_supporter` com `UNIQUE NULLS NOT DISTINCT (contact_id, nucleus_id)`, consent por chaves estáveis `apoiador-cadastro` / `apoiador-intencao-voto` via `campaignConsent.ts` genérico (`getConsentByKey` / `requireConsentByKey`, falha fechada), actions (create / intenção de voto / import CSV só `geral` / `removeSupporterData`), UI `/campanha/apoiadores` (lista+KPIs, ficha, wizard de import, kit mínimo `wa.me`). Telefone obrigatório no v1; `lideranca` sem acesso à área. Engenharia pronta e mesclada — produção com dados reais espera deploy (build Vercel aplica a migration) + Consent keys + aprovação jurídica (Onda 0). [Plano](plans/cadastro-nominal-apoiadores.md).
|
||||
- **C3 Eventos / agenda de mobilização** — collection `actionPlan` + vertical `/campanha/planos` (lista com tabs Próximos/Todos/Realizados/Rascunhos, detalhe com tabs Visão geral/Tarefas/Atualizações, forms novo/editar), blocos "Próximos eventos" no overview de núcleos e no dashboard; `startAt` opcional só em rascunho (obrigatório ao sair de `rascunho`); access por `coordinators`/`leadership` (escopo `lideranca` só toggle `tasks.done` + append `updates`); transações via `withPayloadTransaction`; migration `20260718_222832_add_action_plan`. Sem `Consent` (dado interno de staff). [Plano](plans/eventos-agenda-mobilizacao.md).
|
||||
- **C7 Escala e DRY pós-C3 — entregue (2026-07-19)** — composição territorial (`useCampaignTerritoryFieldsState` + `CampaignTerritoryCoreFields`), `AsyncSearchCombobox`, leituras por aba + `taskProgress` + typeahead de liderança, short-circuit de hooks em toggle/append; primeira fatia anterior: FormData território, contadores lista, locks, `contactSearchQuery`. Débitos pós-`/simplify` → C11. [Plano](plans/escala-dry-pos-c3.md).
|
||||
- **C6 Escala e DRY pós-C2 — implementado e mesclado em `main` (2026-07-19)** — import bulk drizzle, token HMAC + `supporterImportBatch`, KPI aggregate, shells compartilhados (`campaignListUrl`, `CampaignListPagination`, `campaignFormFields`, `mapCampaignFormActionError`). Duas passagens `/simplify`; débitos maiores → C8. [Plano](plans/escala-dry-pos-c2.md).
|
||||
- **C8 Escala e DRY pós-C6 — implementado e mesclado em `main` (2026-07-19)** — locks advisory em 1 RT, bulk com `.returning()` + leituras drizzle na txn, overview sem `COUNT(*)` redundante, migration `pg_trgm` em `contact`, `drizzleBulk.ts`, `getCoordinatorNucleusIds`, migração da maioria dos `formActions` para `mapCampaignFormActionError`. Duas passagens `/simplify` (pré- e pós-rebase com C7); débitos maiores → C9. [Plano](plans/escala-dry-pos-c6.md).
|
||||
- **C9 Escala e DRY pós-C8 — implementado (2026-07-19)** — filtro unificado Payload↔SQL (`supporterListFilters`), `contactSearchQuery` no aggregate/lista, `loadSupportersPageData` (1× núcleos do coordenador), `apoiadores/[id]/formActions` no mapper compartilhado, `fieldError` nos componentes de form restantes. Passagem `/simplify`; débitos maiores → C10. [Plano](plans/escala-dry-pos-c8.md).
|
||||
- **C10 Escala e DRY pós-C9 registrado (2026-07-19)** — dedup de access na lista de apoiadores (`req.context`), query única de núcleos do coordenador (`loadCoordinatorNucleusScope`), `errorProps` nos forms grandes, prefetch opcional na criação. [Plano](plans/escala-dry-pos-c9.md).
|
||||
- **C11 Escala e DRY pós-C7 registrado (2026-07-19)** — feed de atualizações O(n) (paginação / `actionPlanUpdate` condicional), loaders do detalhe + `loadCampaignUserNamesById`, selects por aba, hot paths de mutação/form (`serializeTasks`, `mutationKind` tipado), utilities de busca/label. [Plano](plans/escala-dry-pos-c7.md).
|
||||
|
||||
### Ciclo 3 — Trilha E: mapa de projeção por município (registrado 2026-07-19)
|
||||
|
||||
Objetivo: equiparar e superar as planilhas "Mapa de projeção de votos Solla 2026" (`docs/sheets/`) dentro do produto — metas em cenários, prioridade, série histórica com tendência, dobradinhas/encaminhamentos manuais e import único da planilha. Decisões de produto (2026-07-19): **sem entidade nova por município** (`electoralNucleus` absorve a camada estratégica) e **série 2014/2018 vem do TSE oficial** (estende o seed A3), não dos números da planilha. [Plano único](plans/mapa-projecao-municipios.md) para os cinco itens:
|
||||
|
||||
- **E1 Metas em cenários + prioridade** — grupo `voteGoals` (`good`/`regular`/`minimum`) + `priority` (`alta|normal`) em `electoralNucleus` (migration compartilhada com E3); card "Metas 2026" no detalhe, filtro na lista, somas no overview B1 e no dashboard (meta estadual derivada). **Implementado e mesclado em `main` 2026-07-19** (migration `20260719_054522_add_nucleus_goals_strategy`; `/simplify` → E6).
|
||||
- **E6 Escala e DRY pós-E1+E3 registrado (2026-07-19)** — débitos do `/simplify` que não entraram no cleanup: query duplicada lista+overview, re-resolve de geografia no baseline do overview, aggregate SQL de metas/prioridade (padrão C6), DRY `formatElectionNumber`/`campaignPercentage`/`voteGoalScenarioLabels`, tipos do dashboard por papel. [Plano](plans/escala-dry-pos-e1.md).
|
||||
- **E2 Série histórica TSE 2014/2018 + tendência** — seed `pnpm db:seed:tse -- --year=2014|2018` (federal T1 BA only); `computeVoteTrend` ±10% (`increase|stable|decline|noBaseline`); série + badge no baseline card, insight e card de distribuição no overview; query candidate-only multi-ano (sem migration). **Implementado 2026-07-19.** Passagem `/simplify` no branch; débitos maiores → E7.
|
||||
- **E7 Escala e DRY pós-E2 registrado (2026-07-19)** — débitos do `/simplify` que não entraram no cleanup: trend VM único no detalhe (sem série duplicada baseline+insights), testes int de `loadNucleusListElectionOverview` (gap + tendência **+ conversão A5-1**), tipo único `VoteTrendDistribution`. [Plano](plans/escala-dry-pos-e2.md).
|
||||
- **E3 Dobradinhas + encaminhamentos manuais** — `dobradinhaNotes` e `nextSteps` staff-only na inteligência do núcleo (excluídos dos view models de `lideranca`); versão manual imediata que A6/C4 complementam depois. **Implementado e mesclado em `main` 2026-07-19** (mesma migration E1+E3; UI em `NucleusIntelligenceDialog` + cards staff na Visão geral).
|
||||
- **E4 Import único da planilha** — `pnpm db:seed:mapa` idempotente por slug, município canônico via `canonicalizeMunicipalityName`; lideranças/assessores da planilha entram como **texto staff-only, nunca `Contact`/`leadership`** (sem telefone/consent).
|
||||
- **E5 Salvador por bairro** — registrado como futuro (exigiria `votacao_secao` + DE-PARA local↔bairro versionado); a visão por zona já é coberta pelo baseline município×zona.
|
||||
|
||||
### Referências de design (UX Pilot, 2026-07-18)
|
||||
|
||||
Os designs gerados pelo UX Pilot para os próximos ciclos estão em [`docs/design-refs/latest/`](design-refs/latest/) (pares `.png` + `.html` com o mesmo nome). **A UX/estrutura é a referência a seguir; a paleta não é** — os arquivos usam a paleta antiga (vermelho escuro `#8E0E23`, navy `#1B2B4B`, dourado `#C8874B`); toda implementação usa os tokens claros do tema `data-theme='campaign'` (`src/app/(frontend)/styles.css`: fundo branco, primário `#C51414`, superfícies neutras). Cada plano detalha o uso na sua seção "Referência visual (UX Pilot)".
|
||||
|
||||
| Item do roadmap | Design | Plano com instruções |
|
||||
| ---------------------------------------- | -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| B1 Overview da lista de núcleos | `Lista-Nucleos-Overview` | [overview-lista-nucleos.md](plans/overview-lista-nucleos.md) |
|
||||
| C1 Compartilhar página | `Compartilhar-Nucleo` | [compartilhar-pagina.md](plans/compartilhar-pagina.md) |
|
||||
| A1 Território multi-município/bairro | `Formulario-Territorio` (bloco Território) | [territorio-multi-municipio-bairro.md](plans/territorio-multi-municipio-bairro.md) |
|
||||
| A2 Zonas TSE + sugestões cruzadas | `Formulario-Territorio` (seção Zonas TSE + chips `{rótulo} +`) | [zonas-por-municipio.md](plans/zonas-por-municipio.md) |
|
||||
| A3/A4 Baseline TSE 2022 + Gap vs 2022 | `Baseline-Eleitoral-2022` (+ bloco "Baseline 2022" em `Lista-Nucleos-Overview`) | [baseline-eleitoral-tse.md](plans/baseline-eleitoral-tse.md) |
|
||||
| A5 Insights (5 planos) | `Baseline-Eleitoral-2022` (card "Insights do território", uma linha por insight) | [conversão](plans/insight-taxa-conversao.md) · [classificação](plans/insight-classificacao-territorial.md) · [alavancagem](plans/insight-alavancagem-chapa.md) (Fase 1 conversão da base + Fase 2 virada: vencedor dir. **ou** share dir. alto) · [mobilização](plans/insight-mobilizacao-brancos-nulos.md) · [competitiva](plans/insight-inteligencia-competitiva.md) |
|
||||
| A7 Escala e DRY pós-A4 | — (sem design novo; reusa baseline A4 + Alert/Progress) | [escala-dry-pos-a4.md](plans/escala-dry-pos-a4.md) |
|
||||
| A8 Perfis do eleitorado (IBGE + manuais) | — (sem design nesta leva; reusa aba Eleitorado + `NucleusIntelligenceDialog`) | [perfil-eleitorado-ibge.md](plans/perfil-eleitorado-ibge.md) |
|
||||
| C2 Cadastro nominal de apoiadores | `Apoiadores-Lista`, `Apoiador-Ficha`, `Importar-CSV-Apoiadores` | [cadastro-nominal-apoiadores.md](plans/cadastro-nominal-apoiadores.md) |
|
||||
| C6 Escala e DRY pós-C2 | — (sem design novo; reusa os de C2 + padrões de núcleos) | [escala-dry-pos-c2.md](plans/escala-dry-pos-c2.md) |
|
||||
| C3 Eventos / agenda de mobilização | `Planos-de-Acao`, `Novo-Plano-de-Acao` | [eventos-agenda-mobilizacao.md](plans/eventos-agenda-mobilizacao.md) |
|
||||
| C7 Escala e DRY pós-C3 | — (sem design novo; reusa os de C3 + padrões de núcleos/C6) | [escala-dry-pos-c3.md](plans/escala-dry-pos-c3.md) |
|
||||
| C8 Escala e DRY pós-C6 | — (sem design novo; reusa os de C2/C6) | [escala-dry-pos-c6.md](plans/escala-dry-pos-c6.md) |
|
||||
| C9 Escala e DRY pós-C8 | — (sem design novo; reusa os de C2/C6/C8) | [escala-dry-pos-c8.md](plans/escala-dry-pos-c8.md) |
|
||||
| C10 Escala e DRY pós-C9 | — (sem design novo; reusa os de C2/C6/C8/C9) | [escala-dry-pos-c9.md](plans/escala-dry-pos-c9.md) |
|
||||
| C11 Escala e DRY pós-C7 | — (sem design novo; reusa os de C3 + padrões de núcleos/C6/C9) | [escala-dry-pos-c7.md](plans/escala-dry-pos-c7.md) |
|
||||
| O0+ Escala e DRY pós-Onda 0 | — (sem design novo; reusa `/privacidade` + padrão revalidate `posts`) | [escala-dry-pos-onda0.md](plans/escala-dry-pos-onda0.md) |
|
||||
| VR+ Escala e DRY pós-visitados | — (sem design novo; reusa dashboard + padrões de lista de núcleos) | [escala-dry-pos-visitados-recentemente.md](plans/escala-dry-pos-visitados-recentemente.md) |
|
||||
| RS+ Escala e DRY pós-reset senha/perfil | — (sem design novo; reusa login + perfil + padrões de form de campanha) | [escala-dry-pos-reset-senha-perfil.md](plans/escala-dry-pos-reset-senha-perfil.md) |
|
||||
| FD+ Escala e DRY pós-Field Desk | — (sem design novo; reusa shells/strips Field Desk) | [escala-dry-pos-field-desk.md](plans/escala-dry-pos-field-desk.md) |
|
||||
| FD2 UX Field Desk pós-critique | — (critique Impeccable; reusa dashboard + badges) | [field-desk-ux-pos-critique.md](plans/field-desk-ux-pos-critique.md) |
|
||||
| B5 Escala e DRY pós-B2 | — (sem design novo; reusa geometrias B2 + padrões de scripts CLI) | [escala-dry-pos-b2.md](plans/escala-dry-pos-b2.md) |
|
||||
| E6 Escala e DRY pós-E1+E3 | — (sem design novo; reusa Metas 2026 + overview B1) | [escala-dry-pos-e1.md](plans/escala-dry-pos-e1.md) |
|
||||
| E7 Escala e DRY pós-E2 | — (sem design novo; reusa Baseline + Insights + overview tendência/conversão) | [escala-dry-pos-e2.md](plans/escala-dry-pos-e2.md) |
|
||||
| D2 Notificações (sino) | `Notificacoes-PWA` (central de notificações) | [notifications.md](plans/notifications.md) |
|
||||
| D1 PWA `/campanha` | `Notificacoes-PWA` (banner "Instale o app da campanha") | [pwa-campanha.md](plans/pwa-campanha.md) |
|
||||
| C5 Operação dia D / GOTV | `Dia-D-GOTV` | sem plano ainda — o design é a primeira materialização do item (check-off de apoiadores por seção/zona, progresso "62% da base", filtro "Ainda não confirmados", estado "Sincronizado" offline-tolerante) |
|
||||
|
||||
**Sem design nesta leva** (encomendar na próxima rodada ou desenhar na implementação): A6 dobradinha 2026, A8 perfis do eleitorado IBGE (seguir aba Eleitorado + card de baseline), B2/B3/B4 mapa da Bahia, C4 demandas, E1–E4 mapa de projeção por município (seguir os padrões de `Baseline-Eleitoral-2022` e `Lista-Nucleos-Overview`), fill-ins (listas globais, reset de senha). O detalhe do plano de ação (tabs Visão geral/Tarefas/Atualizações) e a tela de opt-in de push também não vieram — os planos correspondentes indicam o padrão a seguir.
|
||||
|
||||
### Grafo de dependências
|
||||
|
||||
Setas cheias = dependência dura; tracejadas = dependência suave (melhora, não bloqueia).
|
||||
Setas cheias = dependência dura; tracejadas = suave. Nós ✓ só aparecem se ainda são dep de algum aberto.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph Onda0["Onda 0 — produção"]
|
||||
JUR["Lote jurídico único<br/>(4 Consent keys + aviso privacidade)"]
|
||||
DEPLOY["Checklist deploy + lançamento MVP"]
|
||||
O0DRY["O0+ Escala e DRY pós-Onda 0<br/>(cache / chaves / testes SQL)"]
|
||||
JUR["Lote jurídico final<br/>(Consent + privacidade + RIPD)"]
|
||||
O0DRY["O0+ Escala e DRY pós-Onda 0"]
|
||||
end
|
||||
|
||||
subgraph TrilhaA["Trilha A — dados eleitorais e território"]
|
||||
A1["A1 Território multi-município/bairro<br/>(migration) ✓"]
|
||||
A2["A2 Zonas TSE + sugestões cruzadas<br/>(chips opt-in território↔ZE) ✓"]
|
||||
A3["A3 Baseline TSE 2022 — Fase 1<br/>(import, collections) ✓"]
|
||||
A4["A4 Baseline no produto + Gap vs 2022<br/>(detalhe, overview) ✓"]
|
||||
A7["A7 Escala e DRY pós-A4<br/>(agregar federal / cityCode / Alert)"]
|
||||
A5["A5 Insights: conversão, classificação,<br/>alavancagem (+virada), mobilização, competitiva"]
|
||||
subgraph Preds["Entregues (deps)"]
|
||||
A4["A4 Baseline + Gap ✓"]
|
||||
B1["B1 Overview ✓"]
|
||||
B2["B2 Geometrias ✓"]
|
||||
C2["C2 Cadastro nominal ✓"]
|
||||
C3["C3 Agenda ✓"]
|
||||
C7["C7 Escala pós-C3 ✓"]
|
||||
C9["C9 Escala pós-C8 ✓"]
|
||||
D1["D1 PWA ✓"]
|
||||
E1["E1 Metas + prioridade ✓"]
|
||||
E2["E2 Série + tendência ✓"]
|
||||
E3["E3 Dobradinhas manuais ✓"]
|
||||
end
|
||||
|
||||
subgraph TrilhaA["Trilha A"]
|
||||
A7["A7 Escala/DRY pós-A4"]
|
||||
A5["A5 Insights"]
|
||||
A6["A6 Dobradinha 2026"]
|
||||
A8["A8 Perfis eleitorado IBGE<br/>(médio calculado + manuais)"]
|
||||
A8["A8 Perfis IBGE"]
|
||||
end
|
||||
|
||||
subgraph TrilhaB["Trilha B — superfícies de coordenação"]
|
||||
B1["B1 Overview da lista de núcleos ✓"]
|
||||
B2["B2 Mapa Fase 1 (geometrias) ✓"]
|
||||
B5["B5 Escala e DRY pós-B2<br/>(lazy geometrias / cache CLI)"]
|
||||
B3["B3 Mapa Fase 2 (Leaflet nas superfícies)"]
|
||||
B4["B4 Camada de zonas TSE no mapa"]
|
||||
subgraph TrilhaB["Trilha B"]
|
||||
B5["B5 Escala/DRY pós-B2"]
|
||||
B3["B3 Mapa Leaflet"]
|
||||
B4["B4 Zonas TSE no mapa"]
|
||||
end
|
||||
|
||||
subgraph TrilhaC["Trilha C — operação de campo"]
|
||||
C1["C1 Compartilhar página ✓"]
|
||||
C2["C2 Cadastro nominal de apoiadores ✓"]
|
||||
C6["C6 Escala e DRY pós-C2<br/>(import/listas/forms) ✓"]
|
||||
C3["C3 Eventos / agenda de mobilização ✓"]
|
||||
C7["C7 Escala e DRY pós-C3<br/>(território/contato/leituras/hooks) ✓"]
|
||||
C8["C8 Escala e DRY pós-C6<br/>(locks bulk / leituras / forms DRY) ✓"]
|
||||
C9["C9 Escala e DRY pós-C8<br/>(filtros unificados / loader / forms) ✓"]
|
||||
C10["C10 Escala e DRY pós-C9<br/>(access dedup / scope / errorProps)"]
|
||||
C11["C11 Escala e DRY pós-C7<br/>(feed escala / loaders / hot paths)"]
|
||||
subgraph TrilhaC["Trilha C"]
|
||||
C10["C10 Escala/DRY pós-C9"]
|
||||
C11["C11 Escala/DRY pós-C7"]
|
||||
C4["C4 Demandas"]
|
||||
C5["C5 Operação dia D / GOTV<br/>(proposto, a validar)"]
|
||||
C5["C5 Dia D / GOTV"]
|
||||
C2prod["C2 dados reais"]
|
||||
end
|
||||
|
||||
subgraph TrilhaD["Trilha D — plataforma e engajamento"]
|
||||
D1["D1 PWA /campanha ✓"]
|
||||
D2["D2 Notificações (push + sino)"]
|
||||
subgraph TrilhaD["Trilha D"]
|
||||
D2["D2 Notificações"]
|
||||
end
|
||||
|
||||
subgraph TrilhaE["Trilha E — mapa de projeção por município"]
|
||||
E1["E1 Metas em cenários + prioridade<br/>(migration c/ E3) ✓"]
|
||||
E2["E2 Série TSE 2014/2018<br/>+ tendência ✓"]
|
||||
E7["E7 Escala e DRY pós-E2<br/>(trend VM / int loader / tipos)"]
|
||||
E3["E3 Dobradinhas + encaminhamentos<br/>manuais (staff-only) ✓"]
|
||||
E4["E4 Import único da planilha<br/>(pnpm db:seed:mapa)"]
|
||||
E5["E5 Salvador por bairro<br/>(futuro; validar valor)"]
|
||||
E6["E6 Escala e DRY pós-E1+E3<br/>(aggregate / geo / DRY UI)"]
|
||||
subgraph TrilhaE["Trilha E"]
|
||||
E6["E6 Escala/DRY pós-E1+E3"]
|
||||
E7["E7 Escala/DRY pós-E2"]
|
||||
E4["E4 Import planilha"]
|
||||
E5["E5 Salvador por bairro"]
|
||||
end
|
||||
|
||||
A1 --> A2
|
||||
A1 --> A8
|
||||
A3 --> A4
|
||||
B1 --> A4
|
||||
A2 -.qualidade de tseZones.-> A4
|
||||
A4 --> A7
|
||||
A4 --> A5
|
||||
A4 --> A6
|
||||
A7 -.mesmo loader.-> A5
|
||||
B2 -.codarea IBGE.-> A8
|
||||
TSE2026["TSE publica candidaturas 2026<br/>(externo, após 15/08)"] --> A6
|
||||
B2 -.codarea.-> A8
|
||||
TSE2026["TSE candidaturas 2026<br/>(após 15/08)"] --> A6
|
||||
|
||||
B1 --> B3
|
||||
B2 --> B3
|
||||
B2 --> B5
|
||||
B5 -.lazy geometrias.-> B3
|
||||
A4 -.métricas do coroplético.-> B3
|
||||
A2 --> B4
|
||||
A4 -.coroplético.-> B3
|
||||
B3 --> B4
|
||||
A1 --> C3
|
||||
A1 --> C4
|
||||
C3 --> C7
|
||||
C3 -.relação actionPlan opcional.-> C4
|
||||
C6 -.campaignListUrl.-> C7
|
||||
JUR --> C2prod["C2 em produção<br/>(dados reais)"]
|
||||
C2 --> C2prod
|
||||
C2 --> C6
|
||||
C2 --> C5
|
||||
C6 -.escala da base.-> C5
|
||||
C6 --> C8
|
||||
C8 --> C9
|
||||
|
||||
C9 --> C10
|
||||
C7 --> C11
|
||||
C10 -.forms DRY.-> C11
|
||||
C3 -.relação opcional.-> C4
|
||||
JUR --> C2prod
|
||||
C2 --> C2prod
|
||||
C2 --> C5
|
||||
|
||||
D1 --> D2
|
||||
JUR -.chave de push.-> D2
|
||||
DEPLOY -.engenharia entregue.-> O0DRY
|
||||
A4 --> E2
|
||||
JUR -.chave push.-> D2
|
||||
|
||||
E1 --> E6
|
||||
E6 -.perf lista/overview.-> E4
|
||||
E2 --> E7
|
||||
E1 --> E4
|
||||
E3 --> E4
|
||||
E2 -.tendência no import.-> E4
|
||||
E1 -.metas no coroplético.-> B3
|
||||
E2 -.classe por cor.-> B3
|
||||
E2 -.dataset votacao_secao.-> E5
|
||||
E2 --> E7
|
||||
E7 -.int loader antes.-> E4
|
||||
A7 -.mesmo loader do baseline.-> E2
|
||||
A7 -.mesmo módulo.-> E7
|
||||
E6 -.geo cache.-> E7
|
||||
E6 -.perf lista.-> E4
|
||||
E7 -.int loader.-> E4
|
||||
E2 -.votacao_secao.-> E5
|
||||
E1 -.metas mapa.-> B3
|
||||
E2 -.classe cor.-> B3
|
||||
```
|
||||
|
||||
Itens sem seta de entrada (**paralelizáveis a qualquer momento**): **O0+** (após engenharia Onda 0 ✓), **VR+** (após Visitados recentemente ✓), **RS+** (após Reset senha + perfil ✓), **FD+** / **FD2** (após polish Field Desk em branch) e fill-ins (listas globais, higiene PascalCase). **Entregues e mesclados em `main` em 2026-07-18/19** (✓ no grafo): A1, A2, A3, A4, B1, B2, C1, C2, C3, C6, C7, C8, C9, D1, **E1, E2 e E3** — ver "Ciclo 2", "Ciclo 2+" e "Ciclo 3" acima. Fill-ins entregues em branch local (2026-07-19): Visitados recentemente, Reset senha + foto de perfil. C2 segue com produção bloqueada pelo lote jurídico (Onda 0: Consent keys `apoiador-cadastro` / `apoiador-intencao-voto` + aprovação). Destravados por eles: A5/A6 (após A4 ✓), A7 (após A4 ✓; preferencialmente antes ou com A5/B3/E2), **A8** (após A1 ✓; suave B2 ✓ para `codarea` — paralelizável agora), **E7 (após E2 ✓; Fase 2 int preferível antes de E4)**, **E6 (após E1+E3 ✓)**, **E4 (após E1+E3 ✓; suave: E6 F1 e E7 F2 reduzem custo/risco do overview antes do import em massa)**, B3 (B1 ✓ + B2 ✓; suave: A4 ✓ para coroplético e B5 F1 para lazy load), B5 (após B2; preferencialmente com B3), C4 (A1 + C3 prontos), C10 (após C9 ✓; cortável se a base nominal permanecer pequena), C11 (após C7 ✓; cortável se a agenda permanecer pequena; Fase 1 feed condicional) e D2 push (D1 pronto; falta a chave jurídica).
|
||||
Paralelizáveis a qualquer momento: **O0+**, **VR+**, **RS+**, **FD+** / **FD2** (após polish Field Desk), listas globais, PascalCase.
|
||||
|
||||
### Sequência de execução por janela do calendário
|
||||
### Sequência por janela (só pendentes)
|
||||
|
||||
**Janela 1 — agora → 05/08 (convenções): colocar a vertical em produção e consertar a fundação de dados.**
|
||||
**Janela 1 — agora → 05/08 (convenções): produção com hold de PII.**
|
||||
|
||||
| Ordem | Item | Plano | Depende de | Paralelizável com |
|
||||
| ----- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------ | ------------------------------------- | ----------------- |
|
||||
| 1 | Onda 0 (textos MVP auto + deploy + hold PII real) | [detalhes](plans/onda-0.md) | engenharia entregue; jurídico final | tudo |
|
||||
| 2 | A1 Território multi-município/bairro **(entregue 2026-07-18)** | [detalhes](plans/territorio-multi-municipio-bairro.md) | — | B1, C1, A3 |
|
||||
| 3 | A2 Zonas TSE + sugestões cruzadas (chips opt-in território↔ZE) **(entregue 2026-07-18)** | [detalhes](plans/zonas-por-municipio.md) | A1 (nasce contra `cities[]`) | B1 ✓, C1 ✓, A3 ✓ |
|
||||
| 4 | B1 Overview da lista de núcleos **(entregue 2026-07-18)** | [detalhes](plans/overview-lista-nucleos.md) | — | A1, A2, C1 |
|
||||
| 5 | C1 Compartilhar página **(entregue 2026-07-18)** | [detalhes](plans/compartilhar-pagina.md) | — | tudo |
|
||||
| 6 | A3 Baseline TSE 2022 — Fase 1 (import) **(entregue 2026-07-18)** | [detalhes](plans/baseline-eleitoral-tse.md) | — (dado público; sem bloqueador LGPD) | tudo |
|
||||
| Ordem | Item | Plano | Depende de | Paralelizável com |
|
||||
| ----- | ---- | ----- | ---------- | ----------------- |
|
||||
| 1 | Onda 0 (lote jurídico final + smoke + hold PII real) | [onda-0.md](plans/onda-0.md) | engenharia entregue | tudo |
|
||||
|
||||
**Janela 2 — 05/08 → 16/08 (pré-propaganda): base nominal + inteligência + agenda prontas para o arranque.**
|
||||
**Janela 2 — 05/08 → 16/08 (pré-propaganda): base nominal + escala restante.**
|
||||
|
||||
| Ordem | Item | Plano | Depende de | Paralelizável com |
|
||||
| ----- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ | ---------------------------------- | ------------------------------------- |
|
||||
| 7 | C2 Cadastro nominal de apoiadores _(engenharia pronta e mesclada em `main` 2026-07-18 — deploy + Consent keys ainda pendentes)_ | [detalhes](plans/cadastro-nominal-apoiadores.md) | produção: lote jurídico (Onda 0) | A4, C3, C6, C7 |
|
||||
| 8 | C6 Escala e DRY pós-C2 (import em massa, KPI, shells compartilhados com núcleos) **(implementado e mesclado em `main` 2026-07-19 — Fases 1–5 + simplify)** | [detalhes](plans/escala-dry-pos-c2.md) | C2 (merge) | A4, C3, C7, C8 |
|
||||
| 9 | A4 Baseline no produto + insight Gap vs 2022 **(implementado e mesclado em `main` 2026-07-18 — Fases 2–4 + simplify)** | [detalhes](plans/baseline-eleitoral-tse.md) | A3 + B1 (suave: A2) | C2, C3, C6, C7, C8 |
|
||||
| 10 | C3 Eventos / agenda de mobilização (`actionPlan`) **(implementado e mesclado em `main` 2026-07-18)** | [detalhes](plans/eventos-agenda-mobilizacao.md) | A1 | C2, A4, C6, C7, C8 |
|
||||
| 11 | C7 Escala e DRY pós-C3 **(entregue 2026-07-19 — Fases 1–5; feed O(n) condicional fora de escopo)** | [detalhes](plans/escala-dry-pos-c3.md) | C3 (suave: C6 F2) | A4, C6, C8, E1+E3, E2 |
|
||||
| 12 | C8 Escala e DRY pós-C6 (locks bulk em 1 RT, leituras/`pg_trgm`, helpers drizzle, forms DRY) **(implementado e mesclado em `main` 2026-07-19 — Fases 1–4 + simplify)** _(cortável se a base nominal permanecer pequena)_ | [detalhes](plans/escala-dry-pos-c6.md) | C6 (merge) | A4, C3, C7, E1+E3, E2, C9 |
|
||||
| 13 | C9 Escala e DRY pós-C8 (filtro unificado Payload↔SQL, `contactSearchQuery` no aggregate, loader único `/apoiadores`, forms restantes) **(implementado e mesclado em `main` 2026-07-19 — Fases 1–4 + simplify)** _(cortável se a base nominal permanecer pequena)_ | [detalhes](plans/escala-dry-pos-c8.md) | C8 (merge) | A4, C3, C7, E1+E3, E2, C10 |
|
||||
| 14 | C10 Escala e DRY pós-C9 (dedup access lista, scope único de núcleos do coordenador, `errorProps` nos forms) _(cortável se a base nominal permanecer pequena; Fase 3 DRY pode entrar isolada)_ | [detalhes](plans/escala-dry-pos-c9.md) | C9 (merge) | A4, C3, C7, E1+E3, E2, C11 |
|
||||
| 15 | C11 Escala e DRY pós-C7 (feed O(n) condicional, loaders do detalhe, selects por aba, hot paths mutação/form, utilities de busca) _(cortável se a agenda permanecer pequena; Fase 1 migration só com volume medido)_ | [detalhes](plans/escala-dry-pos-c7.md) | C7 (merge) | A4, C3, C10, E1+E3, E2 |
|
||||
| 16 | E1 Metas em cenários + prioridade + E3 dobradinhas/encaminhamentos manuais **(implementado e mesclado em `main` 2026-07-19 — migration `20260719_054522`; `/simplify` → E6)** | [detalhes](plans/mapa-projecao-municipios.md) | — | tudo da janela |
|
||||
| 17 | E6 Escala e DRY pós-E1+E3 (aggregate SQL metas/prioridade, geografia baseline, DRY formatação) _(cortável se poucos núcleos ativos)_ | [detalhes](plans/escala-dry-pos-e1.md) | E1+E3 ✓ | E2, C10, A7, E4 |
|
||||
| 18 | E2 Série histórica TSE 2014/2018 + tendência automática (seed por ano; sem migration) **(implementado 2026-07-19; `/simplify` → E7)** | [detalhes](plans/mapa-projecao-municipios.md) | A4 ✓ (suave: A7 F1 — mesmo loader) | E1+E3, E6, E7, C7, C8, C9, C10, C11 |
|
||||
| 19 | E7 Escala e DRY pós-E2 (trend VM único no detalhe, int `loadNucleusListElectionOverview`, tipo `VoteTrendDistribution`) _(cortável se poucos núcleos; Fase 2 int preferível antes de E4)_ | [detalhes](plans/escala-dry-pos-e2.md) | E2 (merge) | E6, A7, E4, C10 |
|
||||
| Ordem | Item | Plano | Depende de | Paralelizável com |
|
||||
| ----- | ---- | ----- | ---------- | ----------------- |
|
||||
| 2 | C2 dados reais _(Consent keys + aprovação)_ | [cadastro-nominal-apoiadores.md](plans/cadastro-nominal-apoiadores.md) | Onda 0 jurídico | C10, C11, E6, E7 |
|
||||
| 3 | C10 Escala/DRY pós-C9 _(cortável se base pequena)_ | [escala-dry-pos-c9.md](plans/escala-dry-pos-c9.md) | C9 ✓ | C11, E6, E7 |
|
||||
| 4 | C11 Escala/DRY pós-C7 _(cortável se agenda pequena)_ | [escala-dry-pos-c7.md](plans/escala-dry-pos-c7.md) | C7 ✓ | C10, E6, E7 |
|
||||
| 5 | E6 Escala/DRY pós-E1+E3 _(cortável se poucos núcleos)_ | [escala-dry-pos-e1.md](plans/escala-dry-pos-e1.md) | E1+E3 ✓ | E7, C10, A7 |
|
||||
| 6 | E7 Escala/DRY pós-E2 _(Fase 2 int preferível antes de E4)_ | [escala-dry-pos-e2.md](plans/escala-dry-pos-e2.md) | E2 ✓ | E6, A7, E4 |
|
||||
|
||||
**Janela 3 — 16/08 → set (campanha de rua): inteligência ampliada, visualização e engajamento.**
|
||||
**Janela 3 — 16/08 → set (campanha de rua): inteligência, mapa, engajamento.**
|
||||
|
||||
| Ordem | Item | Plano | Depende de | Paralelizável com |
|
||||
| ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | -------------------------- |
|
||||
| 18 | E4 Import único da planilha (`pnpm db:seed:mapa`; sem criar `Contact`/`leadership`) | [detalhes](plans/mapa-projecao-municipios.md) | E1 ✓ + E3 ✓ (suave: E2 ✓ — tendência visível na revisão; E6 F1 — perf da lista) | A5, A7, A8, B3, B5, C4, D2 |
|
||||
| 19 | A5 Insights derivados do baseline (5 itens, paralelizáveis entre si; conversão e classificação exigem limiares de produto; alavancagem inclui Fase 2 virada por vencedor **ou** share da direita) | [conversão](plans/insight-taxa-conversao.md) · [classificação](plans/insight-classificacao-territorial.md) · [alavancagem](plans/insight-alavancagem-chapa.md) · [mobilização](plans/insight-mobilizacao-brancos-nulos.md) · [competitiva](plans/insight-inteligencia-competitiva.md) | A4 | A7, A8, B3, B5, C4, E4, D1 |
|
||||
| 20 | A8 Perfis do eleitorado — perfil médio IBGE (Censo) + manuais na aba Eleitorado _(cortável: manuais já existem)_ | [detalhes](plans/perfil-eleitorado-ibge.md) | A1 ✓ (suave: B2 ✓ — `codarea`) | A5, A7, B3, B5, C4, E4, D1 |
|
||||
| 21 | A7 Escala e DRY pós-A4 (agregar federal no detalhe, `cityCode` TSE, Alert `confirmed` + Progress) | [detalhes](plans/escala-dry-pos-a4.md) | A4 ✓ (suave: A5/B3/E2 amplificam o custo) | A5, A8, B3, B5, C4, E4, D1 |
|
||||
| 22 | B2 Mapa Fase 1 (geometrias) **(entregue 2026-07-18)** + B3 Leaflet nas superfícies | [detalhes](plans/mapa-bahia-geometrias.md) | B3 ← B1 ✓ + B2 ✓ (suave: A4 coroplético; E1/E2 métricas; B5 F1 lazy geometrias) | A5, A7, A8, B5, C4, E4, D1 |
|
||||
| 23 | B5 Escala e DRY pós-B2 (lazy geometrias + cache CLI) | [detalhes](plans/escala-dry-pos-b2.md) | B2 ✓ (F1 preferencialmente com B3) | A5, A7, A8, B3, C4, E4, D1 |
|
||||
| 24 | C4 Demandas | [detalhes](plans/demandas-campanha.md) | A1 (suave: C3 para a relação `actionPlan`) | A5, A7, A8, B3, B5, E4, D1 |
|
||||
| 25 | D1 PWA `/campanha` **(entregue antecipado em 2026-07-18)** | [detalhes](plans/pwa-campanha.md) | — | tudo |
|
||||
| 26 | D2 Notificações (push + sino) — sino não depende do PWA; push sim | [detalhes](plans/notifications.md) | D1 ✓ (push) + chave `campanha-notificacoes-push` do lote jurídico | A5, A7, A8, B3, B5, C4, E4 |
|
||||
| Ordem | Item | Plano | Depende de | Paralelizável com |
|
||||
| ----- | ---- | ----- | ---------- | ----------------- |
|
||||
| 7 | E4 Import único da planilha | [mapa-projecao-municipios.md](plans/mapa-projecao-municipios.md) | E1 ✓ + E3 ✓ (suave: E6 F1, E7 F2) | A5, A7, A8, B3, B5, C4, D2 |
|
||||
| 8 | A5 Insights (5, paralelizáveis entre si) | [conversão](plans/insight-taxa-conversao.md) · [classificação](plans/insight-classificacao-territorial.md) · [alavancagem](plans/insight-alavancagem-chapa.md) · [mobilização](plans/insight-mobilizacao-brancos-nulos.md) · [competitiva](plans/insight-inteligencia-competitiva.md) | A4 ✓ | A7, A8, B3, B5, C4, E4, D2 |
|
||||
| 9 | A8 Perfis eleitorado IBGE _(cortável: manuais já existem)_ | [perfil-eleitorado-ibge.md](plans/perfil-eleitorado-ibge.md) | A1 ✓ (suave: B2 ✓) | A5, A7, B3, B5, C4, E4 |
|
||||
| 10 | A7 Escala/DRY pós-A4 | [escala-dry-pos-a4.md](plans/escala-dry-pos-a4.md) | A4 ✓ | A5, A8, B3, B5, C4, E4 |
|
||||
| 11 | B3 Mapa Leaflet | [mapa-bahia-geometrias.md](plans/mapa-bahia-geometrias.md) | B1 ✓ + B2 ✓ (suave: A4, E1/E2, B5 F1) | A5, A7, A8, B5, C4, E4 |
|
||||
| 12 | B5 Escala/DRY pós-B2 | [escala-dry-pos-b2.md](plans/escala-dry-pos-b2.md) | B2 ✓ (F1 c/ B3) | A5, A7, A8, B3, C4, E4 |
|
||||
| 13 | C4 Demandas | [demandas-campanha.md](plans/demandas-campanha.md) | A1 ✓ (suave: C3) | A5, A7, A8, B3, B5, E4 |
|
||||
| 14 | D2 Notificações (push + sino) | [notifications.md](plans/notifications.md) | D1 ✓ + chave push Onda 0 | A5, A7, A8, B3, B5, C4, E4 |
|
||||
|
||||
**Janela 4 — set → 04/10 (reta final): dobradinha, dia D e estabilização.**
|
||||
**Janela 4 — set → 04/10 (reta final): dobradinha, dia D, estabilização.**
|
||||
|
||||
| Ordem | Item | Plano | Depende de |
|
||||
| ----- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
|
||||
| 27 | A6 Insight dobradinha 2026 | [detalhes](plans/insight-dobradinha-2026.md) | A4 + TSE publicar candidaturas 2026 (externo, após 15/08) + taxonomia de alinhamento (produto) |
|
||||
| 28 | B4 Camada de zonas TSE no mapa | ciclo seguinte do [plano do mapa](plans/mapa-bahia-geometrias.md) | A2 + B3 |
|
||||
| 29 | C5 **Operação dia D / GOTV** _(item proposto nesta revisão — validar com produto)_: confirmação de comparecimento da base nominal em 04/10 (lista de apoiadores por núcleo/zona com marcação "confirmou que vai votar / votou", visão de cobertura para a coordenação). É o uso final da base construída em C2; a literatura de campanha trata a mobilização do dia como onde eleições apertadas se decidem. Sem plano ainda. | design: [`Dia-D-GOTV`](design-refs/latest/Dia-D-GOTV.png) | C2 (suave: C6 para escala da base) |
|
||||
| 30 | Congelamento: a partir de ~20/09, só correção de bug e dados; nada de migration arriscada perto do dia D. E5 (Salvador por bairro) fica registrado para depois da eleição, condicionado a decisão de produto. | — | — |
|
||||
| Ordem | Item | Plano | Depende de |
|
||||
| ----- | ---- | ----- | ---------- |
|
||||
| 15 | A6 Insight dobradinha 2026 | [insight-dobradinha-2026.md](plans/insight-dobradinha-2026.md) | A4 ✓ + TSE candidaturas 2026 (após 15/08) + taxonomia produto |
|
||||
| 16 | B4 Camada de zonas TSE no mapa | [mapa-bahia-geometrias.md](plans/mapa-bahia-geometrias.md) | A2 ✓ + B3 |
|
||||
| 17 | C5 Operação dia D / GOTV _(validar com produto; sem plano)_ | design [`Dia-D-GOTV`](design-refs/latest/Dia-D-GOTV.png) | C2 (suave: escala C6+) |
|
||||
| 18 | Congelamento ~20/09: só bugfix + dados. E5 pós-eleição. | — | — |
|
||||
|
||||
**Fill-ins (qualquer janela, quando houver folga; nenhum bloqueia nada):**
|
||||
### Fill-ins abertos
|
||||
|
||||
- **O0+ Escala e DRY pós-Onda 0** (revalidate globals, chaves Consent, testes SQL provision, DRY Lexical/layout) — engenharia pós-MVP; paralelizável com smoke Onda 0. → [detalhes](plans/escala-dry-pos-onda0.md)
|
||||
- **Visitados recentemente** ✓ (client-side, `localStorage`; MVP entregue 2026-07-19). → [detalhes](plans/visitados-recentemente.md)
|
||||
- **VR+ Escala e DRY pós-visitados recentemente** (refresh bfcache/storage, DRY tempo relativo, `CampaignLinkListRow`, shell do dashboard) — engenharia pós-MVP; paralelizável com qualquer janela. → [detalhes](plans/escala-dry-pos-visitados-recentemente.md)
|
||||
- **Reset de senha self-service + foto de perfil** ✓ (Resend + `/campanha/perfil`; MVP entregue 2026-07-19). → [detalhes](plans/reset-senha-foto-perfil.md)
|
||||
- **RS+ Escala e DRY pós-reset senha/perfil** (auth read `depth: 0`, avatar fora da TX, **`CampaignAuthPageShell` ✓ parcial**, schemas de senha unificados, login em `CampaignFormActionState`) — engenharia pós-MVP; paralelizável com qualquer janela. → [detalhes](plans/escala-dry-pos-reset-senha-perfil.md)
|
||||
- **Field Desk polish** (Impeccable quieter→polish; critique 28→32) — shells/strips/freshness/filas-first/Mais filtros/empties; em branch local 2026-07-19. → `PRODUCT.md` / `DESIGN.md`
|
||||
- **FD+ Escala e DRY pós-Field Desk** (`CampaignPageShell` size, heading class, VoteGoals→MetricStrip, datas canônicas, NucleusFilters bag) — engenharia pós-polish; paralelizável com qualquer janela. → [detalhes](plans/escala-dry-pos-field-desk.md)
|
||||
- **FD2 UX Field Desk pós-critique** (bulk na fila Sem coordenador, glossário inline, outline h2 Filas, empty coordenador+CTA, Sidebar motion cortável) — produto/UX pós-critique; _(validar prioridades com produto)_. → [detalhes](plans/field-desk-ux-pos-critique.md)
|
||||
- **Listas globais** (lideranças, atualizações, territórios no nível raiz). _(plano MVP; sem plano detalhado ainda)_
|
||||
- **Higiene de código:** varredura PascalCase dos componentes legados. _(notebook Núcleos)_
|
||||
- **O0+** Escala/DRY pós-Onda 0 — [escala-dry-pos-onda0.md](plans/escala-dry-pos-onda0.md)
|
||||
- **VR+** Escala/DRY pós-visitados — [escala-dry-pos-visitados-recentemente.md](plans/escala-dry-pos-visitados-recentemente.md)
|
||||
- **RS+** Escala/DRY pós-reset senha/perfil — [escala-dry-pos-reset-senha-perfil.md](plans/escala-dry-pos-reset-senha-perfil.md)
|
||||
- **Field Desk polish** — em branch (`PRODUCT.md` / `DESIGN.md`)
|
||||
- **FD+** Escala/DRY pós-Field Desk — [escala-dry-pos-field-desk.md](plans/escala-dry-pos-field-desk.md)
|
||||
- **FD2** UX Field Desk pós-critique — [field-desk-ux-pos-critique.md](plans/field-desk-ux-pos-critique.md)
|
||||
- **Listas globais** (lideranças, atualizações, territórios) — sem plano detalhado
|
||||
- **Higiene PascalCase** dos componentes legados — notebook Núcleos
|
||||
|
||||
**Cortes seguros se o prazo apertar** (nesta ordem): E5 Salvador por bairro (já registrado como futuro), B4 camada de zonas, B5 F2 (cache CLI — duplicação pequena), E4 import da planilha (os campos E1/E3 podem ser preenchidos à mão pela coordenação; o import só acelera), **A8 perfis IBGE** (a aba Eleitorado e `voterProfiles` manuais já existem; o calculado só acelera o ponto de partida), **E7 F1 e F3** (dedup visual/API — Fase 2 int loader é a mais valiosa antes de E4), **E6 F2–F4** (geografia baseline / DRY UI / tipos dashboard — Fase 1 aggregate só se poucos núcleos; preferir E6 F1 antes de E4 ou B3 se a lista ficar lenta), A7 F2–F3 (`cityCode` / Alert DRY — só se a geografia real dos núcleos permanecer 1–2 municípios), E2 série 2014/2018 (reduzível a só 2018 se o formato TSE de 2014 atrasar; a tendência vira 2 pontos), B3 mapa Leaflet (preferir levar B5 F1 lazy load junto se B3 entrar), C4 demandas, D2 push (mantendo o sino), C6 fases 3–5 (import bulk / preview token / KPI — só se a base nominal permanecer pequena), C8 (escala/DRY pós-C6 — só se a base nominal permanecer pequena; Fases 3–4 DRY podem entrar isoladas), C9 (escala/DRY pós-C8 — só se a base nominal permanecer pequena; Fases 3–4 DRY de forms podem entrar isoladas), C10 (escala/DRY pós-C9 — só se a base nominal permanecer pequena; Fase 3 `errorProps` pode entrar isolada; Fase 1 access dedup é a mais valiosa se coordenadores usarem `/apoiadores` com frequência), C11 (escala/DRY pós-C7 — só se a agenda permanecer pequena; Fase 1 migration `actionPlanUpdate` só com volume medido; Fases 2–3 loaders/selects são as mais valiosas se `/campanha/planos` tiver uso real), **O0+ Fase 4–5** (Lexical/layout shell e Footer condicional — Fases 1–3 preferíveis antes de tráfego real em `/privacidade`), **VR+ Fases 3–5** (row shell, layout wrapper, micro-perf — Fase 1 refresh bfcache é a mais valiosa se usuários reclamarem de painel stale), **RS+ Fases 3–5** (shell auth / campos de senha compartilhados / avatar intent único — Fase 1 auth read é a mais valiosa se `/campanha` ficar lento), **FD+ Fases 3–5** (VoteGoals strip / datas / filtros bag — Fases 1–2 shell size + heading são as de maior ROI de consistência), **FD2 Fase 5** (Sidebar motion — cortável; preferir Fases 1–2 bulk+glossário se Alex reclamar da fila), fill-ins (D1 PWA já entregue, saiu da lista de cortes). Preferir manter C6 fases 1–2, C7 (entregue), C8 fases 1–2 (perf import/lista), C9 Fase 1 (filtro unificado — evita drift KPI/lista), C10 Fase 1 (access dedup na lista), C11 Fases 2–3 (loaders/selects por aba) se a agenda for usada antes de 16/08, **O0+ Fase 1** (revalidate globals pós-migration), **RS+ Fase 1** (auth read sem join de avatar em rotas operacionais), **E6 Fase 1** (aggregate metas/prioridade — evita dupla query na lista antes de E4/B3), **E7 Fase 2** (int do loader combinado gap+trend — evita regressão silenciosa antes de E4) e **A7 F1** (agregar federal no detalhe) se A5/B3/E2 forem entrar — o custo de I/O do loader do A4 é herdado por eles. **Não cortáveis:** Onda 0 (jurídico/Consent), C2 cadastro de apoiadores, C3 eventos/agenda, A4 baseline + gap, **E1+E3 metas/prioridade/estratégia manual** — são respectivamente o risco legal, a base de dados, a operação da propaganda, o instrumento de alocação de esforço e a paridade mínima com a planilha que a campanha já usa.
|
||||
### Cortes seguros / não cortáveis
|
||||
|
||||
### Itens consolidados/removidos nesta revisão (2026-07-17)
|
||||
**Cortáveis** (se o prazo apertar): E5; B4; B5 F2; E4 (campos E1/E3 à mão); A8; E7 F1/F3 (manter F2 antes de E4); E6 F2–F4 (preferir F1 se lista lenta); A7 F2–F3; B3 (levar B5 F1 junto se entrar); C4; D2 push (manter sino); C10 / C11 (se base/agenda pequena); O0+ F4–5; VR+ / RS+ / FD+ Fases 3–5; FD2 Fase 5.
|
||||
|
||||
- **"Insight: oportunidade de virada pela chapa"** _(2026-07-19)_ — não virou item paralelo na trilha A: absorvido como **Fase 2** de [alavancagem da chapa](plans/insight-alavancagem-chapa.md). Gatilhos: (A) dep. federal mais votado de direita **ou** (B) participação proporcional da direita nos nominais federais ≥ limiar — ambos com pré-condição de majoritários locais de esquerda; discurso de completar a chapa.
|
||||
- **"Insight: Gap vs 2022" como item separado** — removido: é a Fase 4 do plano de [baseline TSE 2022](plans/baseline-eleitoral-tse.md) (item A4), não um item próprio.
|
||||
- **"Import do cadastro oficial de zonas TSE e/ou polígonos GeoJSON"** — absorvido: o cadastro tabular é o plano [zonas-por-municipio](plans/zonas-por-municipio.md) (A2); os polígonos são a camada de zonas do [plano do mapa](plans/mapa-bahia-geometrias.md) (B4).
|
||||
- **"Sugestões cruzadas no formulário de território"** (chips `{Município} +` / `{TI} +` em ZEs; irmãos do TI e cidades da ZE em Municípios) — absorvido em A2 na revisão 2026-07-18; substitui o auto-preenchimento forçado + read-only da versão 2026-07-17 do mesmo plano.
|
||||
- **"Notificações WhatsApp Business API"** — movido para fora de escopo: a Meta veda o WhatsApp Business API para campanhas políticas no Brasil e a Res. TSE 23.610 (art. 33) veda disparo em massa (pesquisa em [cadastro-nominal-apoiadores.md](plans/cadastro-nominal-apoiadores.md)). Push + sino ([notifications.md](plans/notifications.md)) cobrem a necessidade.
|
||||
- **"Previsão estatística de votos"** — mantido, mas explicitamente **fora do horizonte deste ciclo eleitoral**: sem dado acumulado suficiente antes de 04/10; o baseline TSE + estimativa manual + insights são o "mínimo honesto" (design-ux §4.5). Reavaliar pós-eleição.
|
||||
- **Dependência baseline → zonas-por-municipio** — rebaixada de dura para suave: `citiesForTerritory` já existe e as rows de `electionTally` resolvem cidade↔zona (ver revisão nos planos).
|
||||
**Preferir manter se houver uso real:** C10 Fase 1 (access dedup); C11 Fases 2–3 (loaders/selects); O0+ Fase 1; RS+ Fase 1; E6 Fase 1; E7 Fase 2; A7 F1 (antes de A5/B3 se I/O do baseline doer).
|
||||
|
||||
**Não cortáveis:** Onda 0 jurídico; C2 dados reais (quando liberado); C3 agenda e A4/E1+E3 já entregues — base legal, base nominal, operação e paridade mínima com a planilha.
|
||||
|
||||
## Bloqueadores atuais
|
||||
|
||||
| Item | Status | Fonte |
|
||||
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | ----------------------------------------------------------------------------------- |
|
||||
| Lote jurídico único LGPD (4 textos de `Consent` + Aviso de Privacidade + avaliação de RIPD) — textos **provisórios MVP** já provisionados por migration ([onda-0.md](plans/onda-0.md)); substituição final + liberação de PII real aguarda assessoria | **caminho crítico para dados reais** | notebook Núcleos; AGENTS checklist; planos cadastro-nominal, notifications e onda-0 |
|
||||
| RBAC em `users` — todo usuário do admin Payload tem acesso total; necessário antes de abrir `/admin` a equipe maior. Nota: o import de apoiadores foi desenhado no `/campanha` justamente para não depender disso | pendente | AGENTS Known Gap #1 |
|
||||
| Fluxos públicos ainda hardcodam ID de Consent (ex. `consent: 2` em `submitWhatsapp.ts`); migrar para chave estável como na campanha | pendente | AGENTS Known Gap #2; plano-arquitetura |
|
||||
| Collection `Pages` inexistente; hero/copy da home ainda hardcoded — bio, propostas e páginas institucionais | pendente (não bloqueia `/campanha`) | AGENTS Known Gap #3 |
|
||||
| Item | Status | Fonte |
|
||||
| ---- | ------ | ----- |
|
||||
| Lote jurídico final LGPD (4 Consent + privacidade + RIPD) — provisórios já provisionados; PII real em hold | **caminho crítico para dados reais** | notebook; onda-0; cadastro-nominal; notifications |
|
||||
| RBAC em `users` (admin Payload) antes de abrir `/admin` a equipe maior | pendente | AGENTS Known Gap #1 |
|
||||
| Fluxos públicos hardcodam ID de Consent → migrar para chave estável | pendente | AGENTS Known Gap #2 |
|
||||
| Collection `Pages` + hero/copy da home editáveis | pendente (não bloqueia `/campanha`) | AGENTS Known Gap #3 |
|
||||
|
||||
## Site público
|
||||
|
||||
**Já entregue:** sistema de notícias/publicações (`post`/`tag`), listagens e artigos, seed a partir de jorgesolla.com.br, cache com tag `posts`; **página `/privacidade`** via global `privacy-policy` (texto provisório Onda 0 — revisão jurídica pendente).
|
||||
**Já entregue:** `post`/`tag` (notícias), seed jorgesolla.com.br, cache `posts`; `/privacidade` (texto provisório Onda 0).
|
||||
|
||||
**Próximos** (não bloqueiam `/campanha`, exceto a política de privacidade, que entra no lote jurídico da Onda 0):
|
||||
**Próximos** (não bloqueiam `/campanha`, exceto textos finais de privacidade ↔ Onda 0):
|
||||
|
||||
- Página de política de privacidade institucional (LGPD) — **MVP entregue** (`/privacidade`); textos finais e polish pós-simplify (revalidate globals, DRY Lexical) em [escala-dry-pos-onda0.md](plans/escala-dry-pos-onda0.md). _(plano-arquitetura §3.1)_
|
||||
- Modelar e popular `Pages` para conteúdo institucional (biografia, mandato, propostas). _(AGENTS Known Gap #3; plano-arquitetura §3)_
|
||||
- Tornar editáveis título/subtítulo (e demais textos) da home via global/`Pages`. _(AGENTS Known Gap #3)_
|
||||
- Agenda pública e multimídia: preferir links oficiais (Câmara, YouTube, Flickr). _(plano-arquitetura §3.1)_
|
||||
- Garantir superfície clara de CTA "Doar" → QueroApoiar. _(AGENTS.md; plano-arquitetura)_
|
||||
- Migrar Consent dos fluxos públicos (WhatsApp, petições) para resolução por chave estável. _(AGENTS Known Gap #2)_
|
||||
- Textos finais de privacidade + polish O0+ ([escala-dry-pos-onda0.md](plans/escala-dry-pos-onda0.md))
|
||||
- `Pages` institucional (bio, mandato, propostas) + home editável
|
||||
- Agenda/multimídia via links oficiais; CTA "Doar" → QueroApoiar
|
||||
- Consent público por chave estável (Known Gap #2)
|
||||
|
||||
## Admin Payload
|
||||
|
||||
- Introduzir `roles` em `users` e access control real antes de ampliar quem entra em `/admin`. _(AGENTS Known Gap #1)_
|
||||
- Seed reproduzível de documentos `Consent` resolvidos por chave (não por ID numérico). _(plano-arquitetura §2.3)_
|
||||
- `roles` em `users` + access control real (Known Gap #1)
|
||||
- Seed reproduzível de `Consent` por chave (não por ID)
|
||||
|
||||
## Plataforma white-label
|
||||
|
||||
- Fase 2 do README: multi-tenant, customização de marca/conteúdo por mandato, módulos compartilhados de comunicação e engajamento.
|
||||
- **Depois da eleição** — monorepo e white-label deliberadamente fora de escopo até lá. _(README Phase 2; plano-arquitetura §6)_
|
||||
- Fase 2 README: multi-tenant / marca por mandato — **depois da eleição**. _(README Phase 2)_
|
||||
|
||||
## Fora de escopo (por enquanto)
|
||||
|
||||
- Serviço Rust (ou outro backend) separado para `/campanha`. _(AGENTS.md; plano-arquitetura)_
|
||||
- Migração self-host / Coolify enquanto a Vercel atender. _(AGENTS.md)_
|
||||
- Processamento de pagamentos ou doações dentro deste app. _(AGENTS.md)_
|
||||
- Tratar Núcleo Eleitoral como sinônimo de Zona Eleitoral do TSE. _(notebook; plano MVP)_
|
||||
- PWA do site público ou do `/admin` — só a vertical `/campanha` será instalável. _(decisão de produto 2026-07-17)_
|
||||
- **PostGIS** até surgir necessidade de query espacial real; v1 do mapa usa TopoJSON estático versionado no repo. _(decisão de produto 2026-07-17)_
|
||||
- **WhatsApp Business API** — vedado para campanhas políticas pela Meta e sem caminho legal para disparo em massa (Res. TSE 23.610 art. 33); superado por push + sino. _(revisão 2026-07-17)_
|
||||
- **Disparo em massa de mensagens** em qualquer canal — mobilização é orgânica (kits de compartilhamento individuais, art. 33 §2º). _(plano cadastro-nominal)_
|
||||
- Previsão estatística de votos neste ciclo eleitoral. _(revisão 2026-07-17; reavaliar pós-eleição)_
|
||||
- Serviço Rust separado; self-host/Coolify; doações in-app. _(AGENTS.md)_
|
||||
- Núcleo = Zona TSE; PWA do site/`/admin`; PostGIS no v1 do mapa.
|
||||
- WhatsApp Business API / disparo em massa; previsão estatística de votos neste ciclo.
|
||||
|
||||
## Fontes
|
||||
|
||||
- [`AGENTS.md`](../AGENTS.md) — decisões travadas, Known Gaps, checklist de campanha
|
||||
- [`.cursor/rules/projects/nucleos-eleitorais.mdc`](../.cursor/rules/projects/nucleos-eleitorais.mdc) — status e decisões do MVP de Núcleos
|
||||
- [`README.md`](../README.md) — missão e direção de produto
|
||||
- [`docs/plans/*.md`](plans/) — planos detalhados por item
|
||||
- `docs/sheets/*.xlsx` — planilhas "Mapa de projeção de votos Solla 2026" (fonte da Trilha E; estrutura de referência da campanha, não fonte de dados eleitorais — estes vêm do TSE)
|
||||
- Plano Cursor `núcleos_eleitorais_mvp_*.plan.md` (fora do repo; workspace local)
|
||||
- `plano-arquitetura-campanha-2026.md` e `design-ux-campanha.md` (Cowork / pasta irmã; fora do repo)
|
||||
- Res. TSE 23.760/2026 (calendário eleitoral) — https://www.tse.jus.br/legislacao/compilada/res/2026/resolucao-no-23-760-de-2-de-marco-de-2026
|
||||
- Res. TSE 23.610/2019 (propaganda; art. 33 — disparo em massa) e Lei 9.504/1997
|
||||
- Politipédia AVM — planejamento de campanha, base de dados, territorialização, operação do dia da eleição — https://politipedia.wiki.br
|
||||
- [`AGENTS.md`](../AGENTS.md) — decisões travadas, Known Gaps, checklist
|
||||
- [`.cursor/rules/projects/nucleos-eleitorais.mdc`](../.cursor/rules/projects/nucleos-eleitorais.mdc) — status operacional
|
||||
- [`README.md`](../README.md) — missão e Phase 2
|
||||
- [`docs/plans/*.md`](plans/) — planos por item
|
||||
- `docs/sheets/*.xlsx` — planilhas Mapa de projeção (estrutura de referência; dados eleitorais = TSE)
|
||||
- Res. TSE 23.760/2026 (calendário) · Res. TSE 23.610/2019 art. 33 · Politipédia AVM
|
||||
|
|
|
|||
|
|
@ -8,3 +8,5 @@ Provisioning runs via:
|
|||
- CLI `pnpm db:seed:consent` (re-aplicar após editar os textos)
|
||||
|
||||
Keys: `lideranca-autopreenchimento`, `apoiador-cadastro`, `apoiador-intencao-voto`, `campanha-notificacoes-push`, plus Global `privacy-policy`.
|
||||
|
||||
These are **provisional MVP** texts. Real leadership/supporter PII stays on hold until electoral counsel approves final copy — see [`docs/plans/onda-0.md`](../../docs/plans/onda-0.md) and [`docs/roadmap.md`](../../docs/roadmap.md) § Onda 0.
|
||||
|
|
|
|||
Loading…
Reference in a new issue