Merge branch 'cursor/63b4db6b'

This commit is contained in:
Francisco Ignácio Fontoura Solla 2026-07-24 14:08:40 -03:00
commit 39cbce8d79
3 changed files with 118 additions and 1 deletions

View file

@ -7,7 +7,9 @@ alwaysApply: false
## Status
- **Fill-in registrado (2026-07-24):** seletor **Cenário** de `/campanha/municipios` → fileira de `MunicipalityFilters` (provider envolve filtros+resultados; overview sem seletor). Plano: [`cenario-junto-filtros-municipios.md`](../../../docs/plans/cenario-junto-filtros-municipios.md).
- **E4R entregue em código (2026-07-24):** `pnpm db:seed:projecao` (`scripts/import-projecao.mjs` + `src/lib/projectionSheetParse.ts`) importa MAPA GERAL canônico → `expectedVotes` (Bom→otimista, Regular→média, Mínimo→pessimista) + `priority` com **overwrite-always** (re-seed quando chegar planilha nova); Salvador pulado; zero PII; dry-run + runbook. No mesmo dia o grupo duplicado `voteGoals` foi removido do app (ver Decisões). Seed local: arquivo `docs/sheets/Mapa projeção de votos Solla 2026.xlsx`, **189** municípios com estimativas, **50** `priority=alta`, re-run delta=0. Produção: após smoke, `ALLOW_REMOTE_DB=true … pnpm db:seed:projecao -- --dry-run` → apply. Plano: [`import-planilha-projecao.md`](../../../docs/plans/import-planilha-projecao.md).
- **Sync roadmap × sessão de campo (2026-07-24):** os achados da sessão real do CG (2026-07-23, `docs/CUSTOMER.md`) foram incorporados ao roadmap e aos planos de inteligência — âncora da mesa = **% da própria votação** (A11 entrega a lente; E9/E10/B13 expõem ao lado de LQ/captura); piso 150 mil resolve a meta inicial de E8; O6 vira **E16 dossiê do município** + gap **G11 emendas por município**; O7 confirma C12 não cortável; E13 rebaixado na fila de corte ("perna" é a restrição dominante); gatilho de D3 reformulado (deltas do ZAP não absorvidos pelo fluxo sede-digita/C12); FD2 absorvido por R6; O8 "ilhas isoladas" em A validar. **Decisão de produto: E4 reaberto como E4R** — seed único via script das planilhas de `docs/sheets/` (arquivo com espaços = canônico de estratégia; underscore = referência Salvador; v1 só `voteGoals`/`priority`, sem PII; Salvador pulado com relatório). Novos itens: **E4R** (janela 1, não cortável), **A11**, **E17 tabela TI no Início** (pedido do candidato; primeira fatia de E12), **B14 município mais próximo** (geolocalização → atalho no Início staff; prompt 1×/sessão; client-only). Planos novos: `import-planilha-projecao.md`, `ranking-votos-municipio.md`, `tabela-ti-inicio.md`, `dossie-municipio.md`, `municipio-mais-proximo.md`.
- **DEPLOY EM PRODUÇÃO REALIZADO (2026-07-23).** A cadeia destrutiva da remodelagem (`20260721_020109_remodel_municipalities` → `20260723_200000_remodel_municipalities` + `20260723_202000_reconcile_municipality_remodel`) foi aplicada em produção pelos builds Vercel de 2026-07-23 (deploys production READY dos commits `a7b8f1b`/`c7a1b74`/`59868a9`). **Este item supersede toda menção "deploy pendente" nas entradas abaixo.** Pendências operacionais: smoke pós-deploy + onboarding do time (Onda 0 §2/§4 em [`docs/roadmap.md`](../../../docs/roadmap.md)); hold de PII real segue no lote jurídico.
- **Hardening de engenharia entregue e em produção (2026-07-23, Fases 0–6):** (0) access lockdown — `users`/`signature`/`subscription`/`consent` admin-only, escrita de CMS (`post`/`tag`/`media`/`petition`) admin-only, gates de rota do leader em `demandas*`/`planos*`, `createLeaderSupporter` com escopo correto, `candidate` tratado como staff irrestrito em todas as superfícies; (1) tooling gate — knip no CI (`.github/workflows/ci.yml`, mirror GitHub), `pnpm lint --max-warnings=0`, ban `as never` (helpers `hookFilledCreateData`/`stub<T>`), tsconfig consolidado + `noUnusedLocals`, ~12 módulos mortos deletados; (2) caching — artefato TSE commitado (`pnpm build:election-aggregates` → `src/lib/electionAggregates/`, loader `bahiaElectionAggregates.ts`), `unstable_cache` com tag `election-tse` nos loaders de eleições, `loadMunicipalityScope` (React `cache()`) dedupe dashboard+mapa+lista; (3) pending honesto — bug de revalidação do toggle de tarefa, `CampaignListPendingBoundary` esmaecendo resultados, transição no compare do mapa, mapa do dashboard + abas do detalhe em Suspense; (4) state scoping — hover do mapa isolado em `MunicipalityMapSelection`, provider de cenário estreitado, `LeaderContactForm` extraído como ilha cliente; (5) estrutura — `campaignAccess` dividido em `src/utilities/access/*` (re-export preservado), `supporterImport.ts` separado, `runStaffEntityMutation` + `runCampaignRedirectFormAction` dedupando twins e 4 ladders de formActions, rename `PLAZA_*`→`MUNICIPALITY_*` concluído; (6) docs + regra `.cursor/rules/engineering-standards.mdc`. Tracker: [`docs/IMPROVE-CODE-QUALITY-PLAN.md`](../../../docs/IMPROVE-CODE-QUALITY-PLAN.md) · ledger: [`docs/TECH-DEBT.md`](../../../docs/TECH-DEBT.md) · mapa de testes: [`docs/TESTING.md`](../../../docs/TESTING.md).

View file

@ -0,0 +1,114 @@
# Seletor de Cenário junto aos filtros de Municípios
Status: rascunho
Atualizado em: 2026-07-24
Item do roadmap: [docs/roadmap.md](../roadmap.md) (Fill-ins)
Impeccable: B — encaixe em `MunicipalityFilters` + `MunicipalityListOverview` (`/campanha/municipios`); sem rota nova
Appetite: ~0,25–0,5 dia eng; relocação de controle + ajuste do provider; sem migration
Responsável: —
## Design (Impeccable)
Âncoras: `PRODUCT.md` (princípios 2 — clareza sob pressão — e 8 — Feel the action) / `DESIGN.md` (register `product`; Field Desk) · tema `data-theme='campaign'` · shells `MunicipalityFilters`, `VoteEstimateScenarioField`, `MunicipalityEstimateScenarioProvider`.
Na implementação (`implement-roadmap-item`): craft compacto → critique → polish (só posição do controle; sem redesign da lista/overview).
Brief compacto:
- **Persona / contexto:** Assessor / CG monta o recorte de municípios (TI, tipo, prioridade…) e já quer escolher **qual cenário** lê na lista/overview no mesmo gesto — sem descer o olhar até o card de visão geral.
- **Job principal:** um único eixo de controles de leitura da lista: filtros de recorte + cenário de estimativa.
- **Estratégia de cor:** Restrained.
- **Edit where you see:** não — o seletor só troca leitura (estado local A10); mutação de estimativas permanece nos Popovers/forms existentes.
- **Anti-goals:** segundo seletor duplicado overview+filtros; meter Cenário na URL; redesign do layout de filtros; unificar com o seletor do mapa do Início neste item.
## Dados → decisão → apresentação
Dados: N/A — este item **não** muda métricas, agregação nem forma de apresentação; só relocação do controle de leitura já entregue em A10. Overview/lista continuam com número do cenário ativo + faixa secundária.
## Contexto
Em `/campanha/municipios` (staff), o seletor **Cenário** (`VoteEstimateScenarioField`) vive no header de `MunicipalityListOverview` — acima do `CampaignMetricStrip`, **abaixo** da barra de filtros (`MunicipalityFilters`: busca, TI, tipo, assessoria, tendência, prioridade). O estado é local via `MunicipalityEstimateScenarioProvider` (default `central`), sincronizando overview + células da lista; no Início o mapa tem seletor próprio quando Ano=2026.
A página hoje **embrulha o provider só em torno dos resultados**, de propósito — comentário em `municipios/page.tsx`: troca de cenário não deve re-renderizar os filtros. Pedido de produto (2026-07-24): o Cenário deve ficar **junto dos outros filtros** da página.
Polish anterior (notebook 2026-07-23) já tinha movido o seletor do mapa → overview; este item completa o agrupamento mental “controles de recorte/leitura da lista”.
## Objetivos
- Staff em `/campanha/municipios`: seletor **Cenário** renderizado na mesma fileira/região de `MunicipalityFilters` (visível com `showStaffFilters`), reusando `VoteEstimateScenarioField`.
- Remover o seletor do header de `MunicipalityListOverview` (overview continua a reagir ao contexto: strip, labels, flash).
- Expandir o `MunicipalityEstimateScenarioProvider` para envolver **filtros + resultados** quando staff — o controle nos filtros precisa do contexto; um único provider por página.
- Cenário permanece **estado local** (não URL); **Limpar** continua zerando só query params / busca — não reseta cenário.
- Guardrails: sem migration, sem collection, sem Consent, sem server action; semântica A10 intacta; `leader` não vê o seletor (página já redireciona; `showStaffFilters` false).
## Decisões travadas
- **Fill-in com plano próprio (não reabrir A10; não absorver só em R6; não item de trilha B).** Relocação barata, paralelizável, cortável; A10 já entregue a semântica. (2026-07-24, classificação roadmap-item.) **Rejeitado:** fase informal de A10 (mistura entrega fechada com polish de layout); só R6 (atrasa quick win no critique largo); ID B novo (infla grafo por ~½ dia).
- **Provider envolve filtros + `CampaignListResults` (staff).** Necessário para o seletor viver em `MunicipalityFilters`. Custo: troca de cenário re-renderiza a árvore dos filtros — aceitável (controle local, sem navegação). **Rejeitado:** prop-drill `scenario`/`setScenario` sem provider; dois providers; manter seletor no overview *e* nos filtros (duplicata).
- **Cenário continua fora da URL e fora de `buildMunicipalityFiltersKey` / Limpar.** Alinhado à decisão A10 (Ano/Escala locais; só `compare` na URL do mapa). **Rejeitado:** `?estimate=` neste item; Limpar resetar cenário (mistura recorte URL com lente de leitura).
- **Hint:** adaptar copy do overview (hoje `VOTE_ESTIMATE_SCENARIO_OVERVIEW_HINT`) para o contexto dos filtros (“Troca o total da visão geral e os votos na lista…”); mapa no Início permanece com o hint próprio. **Rejeitado:** remover o `CampaignInfoHint`.
- **i18n e naming** (AGENTS.md): identificadores existentes (`VoteEstimateScenarioField`, `MunicipalityEstimateScenarioProvider`); strings “Cenário” / Pessimista / Média / Otimista.
## Questões em aberto
- **Posição exata na fileira (antes ou depois dos selects de recorte)?** **Opções:** A) após Prioridade (fim dos staff filters, antes de Limpar) | B) logo após a busca | C) fileira própria abaixo. **Recomendação:** **A** — Cenário é lente de leitura do resultado, não critério de where; fica com os controles staff sem competir com busca/TI. _(assumido — validar no craft se a fileira quebrar em mobile)_
- **Mapa do Início: espelhar “Cenário nos filtros do dashboard”?** **Opções:** A) neste item | B) fora / R6 se critique pedir. **Recomendação:** **B** — pedido é a página de municípios; mapa já agrupa Cenário com Ano/Escala.
## Abordagem proposta
```mermaid
flowchart LR
page["municipios/page.tsx"] --> provider["MunicipalityEstimateScenarioProvider"]
provider --> filters["MunicipalityFilters + VoteEstimateScenarioField"]
provider --> results["CampaignListResults"]
results --> overview["MunicipalityListOverview sem seletor"]
results --> list["MunicipalityList células"]
filters -.setScenario.-> provider
provider -.scenario.-> overview
provider -.scenario.-> list
```
Componentes:
- **`MunicipalitiesPage`** (`src/app/(campaign)/campanha/(app)/municipios/page.tsx`): mover `MunicipalityEstimateScenarioProvider` para envolver `{filters}` + `{CampaignListResults…}` quando `isStaffView`; atualizar o comentário (provider deixa de ser “só results”).
- **`MunicipalityFilters`** (`src/components/campaign/MunicipalityFilters.tsx`): se `showStaffFilters`, renderizar `VoteEstimateScenarioField` na fileira (id estável, ex. `municipality-filter-estimate-scenario`); consumir `useMunicipalityEstimateScenario()` — só montar essa página staff com provider ao redor.
- **`MunicipalityListOverview`** (`src/components/campaign/MunicipalityListOverview.tsx`): remover o bloco do seletor no header; manter `useMunicipalityEstimateScenario`, strip, flash e métricas.
- **`VoteEstimateScenarioField`**: reusar; ajustar hint exportado se o texto ainda falar só de “visão geral” como locus do controle.
- **Migration**: Sem migration, sem collection, sem server action.
Depth check: reusa field + context + filters existentes; sem hook/shared shell novo (&lt;3 call sites de “filtro+cenário”).
## Dependências
- Nenhuma de outro plano aberto. Reusa A10 entregue (`voteEstimate`, `MunicipalityEstimateScenarioProvider`, `VoteEstimateScenarioField`) e o fill-in filtros-auto ([filtros-auto-pracas.md](filtros-auto-pracas.md)).
## Não escopo
- Seletor de Cenário no mapa do Início / `MunicipalityMapPanel` (permanece com Ano=2026) — R6 ou pedido futuro.
- Persistência de cenário em URL, cookie ou perfil.
- Mudança de agregação, default `central`, ou assimetria leader.
- Redesign do overview / metric strip / flash.
- Unificar `MunicipalityFilters` com filtros de outras listas.
## Rabbit holes
- **`?estimate=` na URL “só para share”.** Explode RSC/key de filtros e diverge do mapa. **Mitigação:** Decisão travada — estado local; gatilho só com pedido explícito de share de cenário.
- **Provider global de cenário em todo `/campanha`.** Acopla Início↔lista sem necessidade. **Mitigação:** provider só na página de municípios (como hoje, com escopo ampliado na página).
- **Tratar Cenário como filtro de where / incluir em Limpar.** Muda semântica A10. **Mitigação:** fora de `MunicipalityListState` e de `hasActiveFilters`.
## Adiado com gatilho
- **Mesma colocação mental no dashboard (mapa).** Revisitar quando: R6 critique o Início ou produto pedir “um só lugar para Cenário” entre mapa e lista.
## Referências
- `docs/roadmap.md` (Fill-ins abertos)
- `src/app/(campaign)/campanha/(app)/municipios/page.tsx` — composição filters / provider / results
- `src/components/campaign/MunicipalityFilters.tsx` — fileira de controles
- `src/components/campaign/MunicipalityListOverview.tsx` — seletor atual + flash
- `src/components/campaign/VoteEstimateScenarioField.tsx` — controle reutilizado
- `src/components/campaign/MunicipalityEstimateScenarioContext.tsx` — estado local
- [cenarios-estimativa-votos.md](cenarios-estimativa-votos.md) (A10) — semântica e default
- [filtros-auto-pracas.md](filtros-auto-pracas.md) — precedente de fill-in na mesma superfície
- AGENTS.md — naming; campaign staff vs leader
- `PRODUCT.md` / `DESIGN.md` — Field Desk, Feel the action

View file

@ -1,6 +1,6 @@
# Roadmap — Teqo
Atualizado em: 2026-07-24 (E4R entregue em código — seed local; B14 município mais próximo no roadmap; janela 1: smoke pós-deploy, A11/E17, R6, E8 — [CUSTOMER.md](CUSTOMER.md))
Atualizado em: 2026-07-24 (fill-in: seletor Cenário junto aos filtros em `/campanha/municipios` — [plano](plans/cenario-junto-filtros-municipios.md); E4R ✓; B14; janela 1: smoke, A11/E17, R6, E8)
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).
@ -86,6 +86,7 @@ O discovery literatura→persona→entrevista ([relatório aprovado](research/re
### Fill-ins abertos
- **Cenário junto aos filtros** em `/campanha/municipios` — mover `VoteEstimateScenarioField` para a fileira de `MunicipalityFilters`; overview só consome o contexto · ~0,25–0,5d · Impeccable B · [plano](plans/cenario-junto-filtros-municipios.md)
- **O0+** escala/DRY pós-Onda 0 · [plano](plans/escala-dry-pos-onda0.md)
- **RS+** auth read leve + shells de senha · [plano](plans/escala-dry-pos-reset-senha-perfil.md)
- **C10 / C11** escala apoiadores/planos · reavaliar nas superfícies remodeladas · gatilhos originais intactos: base nominal crescendo ([plano](plans/escala-dry-pos-c9.md)) / volume de planos medido ([plano](plans/escala-dry-pos-c7.md))