From f93eaa7cb587b39a3085732cc3e2963e4ce31e51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Ign=C3=A1cio=20Fontoura=20Solla?= Date: Thu, 30 Jul 2026 02:43:01 -0300 Subject: [PATCH] docs(roadmap): register B77 single-screen vote wizard (B77) Replace the three-step ?cenario= ritual with one screen aligned to the municipality list popover; plan covers focused shortcuts and confirm-time coherence validation. --- .cursor/rules/projects/nucleos-eleitorais.mdc | 1 + docs/plans/ajuste-votos-wizard.md | 4 + docs/plans/simplificar-ajuste-votos-wizard.md | 121 ++++++++++++++++++ docs/roadmap.md | 17 ++- 4 files changed, 137 insertions(+), 6 deletions(-) create mode 100644 docs/plans/simplificar-ajuste-votos-wizard.md diff --git a/.cursor/rules/projects/nucleos-eleitorais.mdc b/.cursor/rules/projects/nucleos-eleitorais.mdc index 8f35893a..c873580c 100644 --- a/.cursor/rules/projects/nucleos-eleitorais.mdc +++ b/.cursor/rules/projects/nucleos-eleitorais.mdc @@ -7,6 +7,7 @@ alwaysApply: false ## Status +- **B77 registrado (2026-07-30):** simplificar wizard ajuste de votos — três cenários numa tela (strip + inputs como popover da lista); atalhos no input focado; coerência só no confirm com highlight; substitui ritual `?cenario=` de B61/B76. Plano: [`simplificar-ajuste-votos-wizard.md`](../../../docs/plans/simplificar-ajuste-votos-wizard.md). - **B71 entregue em código (2026-07-30):** polimento visual dos resultados de busca no Início (UX-1) — `src/lib/homeSearchUi.ts` (`HOME_SEARCH_HIT_ROW_WRAPPER_CLASS` = bleed + hover + `focus-within`; heading/list); `HomeSearchHitRow` faixa full-bleed `-mx-4 px-4 md:-mx-6 md:px-6` (mantém `onMouseDown` preventDefault de B66); todos os `HomeSearch*Group` com `ul list-none` e título `text-xs` sentence-case (sem `uppercase`/`tracking-wide`). Unit `homeSearchUi.unit.spec.tsx`. Sem migration/action/JSON. Plano: [`polimento-resultados-busca-inicio.md`](../../../docs/plans/polimento-resultados-busca-inicio.md). - **B66 entregue em código (2026-07-30):** modo focado da busca no **focus** (emenda B47) — `homeSearchUiFocused` (`inputFocused || query.isActive`) já no contrato/hook; overlap B68 (sugestões no focus vazio). **Nesta entrega:** `CampaignHomeLayout` retrai summary+spacer (`data-slot="home-chrome"`) e actions com grid `0fr/1fr` + opacity 220ms (`motion-reduce:transition-none`) em vez de `hidden`; `md:flex-none` no chrome; `HomeSearchHitRow` `onMouseDown` preventDefault (blur→click); e2e focus sem digitar + regressão com digitação. Sem migration. Plano: [`modo-focado-busca-no-focus.md`](../../../docs/plans/modo-focado-busca-no-focus.md). - **B61 entregue em código (2026-07-29):** etapa de ajuste de votos no wizard A1 — `WizardExpectedVotesStep` substitui o stub B60 em `atualizar-votos` com `?municipio=`; três passos explícitos média→pessimista→otimista via `?cenario=`; atalhos `2×`/±50/±100; validação client com jump+warning (`getWizardVoteViolation`); **um POST batch** ao concluir otimista (`POST /campanha/municipios/expected-votes`). Helpers em `wizardVoteEstimate.ts` + `wizardActionHref` estendido. Sem migration/Consent. Unit `wizardVoteEstimate`; e2e avanço de cenário + violação. Plano: [`ajuste-votos-wizard.md`](../../../docs/plans/ajuste-votos-wizard.md). Desbloqueia wiring A1 / **B64** (prefill votos). diff --git a/docs/plans/ajuste-votos-wizard.md b/docs/plans/ajuste-votos-wizard.md index 3caa3ce5..1edbc6e7 100644 --- a/docs/plans/ajuste-votos-wizard.md +++ b/docs/plans/ajuste-votos-wizard.md @@ -40,6 +40,10 @@ Responsável: — ~~Jump+warning ao confirmar pessimista `> média` quebrava: `useEffect` limpava `violationEditedScenario` na troca de `?cenario=`.~~ **Corrigido em B76** — preservar estado no jump ao cenário violado; limpar ao editar draft/atalho. +### Emenda UX (2026-07-30 → **B77**) + +Ritual de três passos `?cenario=` será **substituído** por uma tela com os três inputs + strip (como popover da lista); validação inline no confirm. Ver [simplificar-ajuste-votos-wizard.md](simplificar-ajuste-votos-wizard.md). + ## Design (Impeccable) Âncoras: `PRODUCT.md` (Feel the action; Clarity under pressure) / `DESIGN.md` · `VoteEstimateScenarioInputs` / `MunicipalityListExpectedVotesControl` · tema `campaign`. diff --git a/docs/plans/simplificar-ajuste-votos-wizard.md b/docs/plans/simplificar-ajuste-votos-wizard.md new file mode 100644 index 00000000..9728705c --- /dev/null +++ b/docs/plans/simplificar-ajuste-votos-wizard.md @@ -0,0 +1,121 @@ +# Simplificar ajuste de votos no wizard (três cenários numa tela) + +Status: rascunho +Atualizado em: 2026-07-30 +Item do roadmap: [docs/roadmap.md](../roadmap.md) (Trilha B, item B77 — UX-1 wizards) +Impeccable: B — encaixe em `WizardExpectedVotesStep` existente; espelha o popover da lista de municípios +Appetite: ~0,75–1 dia eng; substituir ritual `?cenario=` por editor único + confirm; reuso de strip/inputs/atalhos; sem migration +Responsável: — + +## Design (Impeccable) + +Âncoras: `PRODUCT.md` (Clarity under pressure; Feel the action) / `DESIGN.md` · referência visual = `MunicipalityListExpectedVotesControl` + `VoteEstimateScenarioInputs` (`variant="compact"`) · tema `campaign`. + +Na implementação (`implement-roadmap-item`): **craft compacto → critique → polish** (não shape novo — a estrutura já existe na lista). + +Brief compacto: + +- **Persona / contexto:** CG no celular, após escolher município (B60 ✓), quer ajustar Cairu −100 ou Salinas 300→150 sem três confirmações sequenciais. +- **Job principal:** ver e editar **os três cenários juntos**, aplicar atalhos no campo em foco e salvar uma vez. +- **Estratégia de cor:** Restrained; warning de coerência = `Alert` warning/destructive do tema; inputs violados com `ring-destructive` / `border-destructive` — não Signal Red decorativo. +- **Edit where you see:** não — fluxo wizard com **submit explícito** (exceção `campanha-edit-where-you-see`); mas a **forma** do editor copia o popover da lista (não inventar layout novo). +- **Anti-goals:** voltar ao ritual média→pessimista→otimista com `router.replace`; auto-save por cenário; input hero gigante isolado; chart/gauge dos três números. + +## Dados → decisão → apresentação + +- **Vou apresentar dados?** Sim — três inteiros (`expectedVotes` pessimista/média/otimista) e a posição relativa da média entre extremos. +- **Decisões desbloqueadas:** staff define a faixa de projeção da mesa por município antes de seguir o ritual A1 (sinal/tendência B63/B64). +- **Forma escolhida:** **número + contexto** — três inputs lado a lado + **faixa** (`VoteEstimateScenarioStrip`) mostrando onde a média cai entre pessimista e otimista. **Rejeitado:** chart de área; três gauges; % estadual; tabela extra. +- **Profile:** 3 inteiros ≥0, ordem lógica `pessimistic ≤ central ≤ optimistic`; escala relativa só na faixa (min–max dos três preenchidos). +- **Anti-goals de dado:** não misturar `declaredVotes` de liderança; não mostrar cobertura E8 neste passo (wiring A1). + +## Contexto + +**B61** (entregue 2026-07-29) implementou o ajuste de votos no wizard como **três passos** explícitos (`?cenario=central|pessimistic|optimistic`), um input grande por passo, atalhos `2×`/±50/±100 e navegação ao cenário violado com banner (**B76** ✓ corrigiu regressão do banner no jump). O ritual cumpre a decisão travada de B61 (“um cenário por vez”), mas na prática o fluxo ficou **pesado**: três CTAs, três trocas de URL e lógica de violação distribuída entre passos. + +Pedido (2026-07-30): **uma tela** com os três cenários, UI **semelhante ao popover** de votos estimados na lista de municípios (`MunicipalityListExpectedVotesControl`), atalhos no **input selecionado**, validação de coerência **só ao confirmar** — se violar, não avança, mostra warning e **destaca os inputs** que quebram a regra. + +Invariante de servidor permanece: `getVoteEstimateOrderViolation` / `VOTE_ESTIMATE_ORDER_ERROR_MESSAGE` (`src/lib/voteEstimate.ts`). + +## Objetivos + +- Substituir o ritual `?cenario=` em `atualizar-votos` por **um único passo** após `?municipio=`. +- Layout: `VoteEstimateScenarioStrip` (faixa com marcador da média entre extremos) + grid 3 colunas de inputs (labels/aria como no compact da lista). +- Atalhos `2×` · `±50` · `±100` aplicam sobre o **cenário com foco** (`focusedScenario`); default de foco = `central` ao abrir. +- CTA único **“Salvar estimativas →”** (ou `wizardVoteFinalCtaLabel` existente): valida drafts → se violação, **permanece na tela**, `Alert` com mensagem de `getWizardVoteViolation`, inputs violados com estado de erro visual; se OK, **POST batch** igual B61 (`POST /campanha/municipios/expected-votes`). +- Remover navegação `router.replace` entre cenários e estado `violationEditedScenario` / “Voltar para cenário editado” (substituídos por highlight inline). +- `acoes/[slug]/page.tsx`: não ler `?cenario=` para `atualizar-votos`; canonicalizar URLs legadas com `?cenario=` → redirect sem o param (ou ignorar param). +- Staff-only; sem migration / Consent / contrato JSON novo. +- Testes: unit do step (foco + atalho + confirm bloqueado + highlight); atualizar e2e B61 (um passo, violação sem troca de URL). + +## Decisões travadas + +- **Uma tela com três inputs** (emenda explícita à decisão B61 “um cenário por vez”). **Fonte:** pedido CG 2026-07-30 após uso do ritual de três passos. **Rejeitado:** manter três passos com polish incremental (B76 já mostrou que a complexidade de estado entre URLs é frágil). +- **Validação só no confirm** — não bloquear atalho nem `onChange` por ordem. **Rejeitado:** validação per-keystroke (atrasa atalhos); jump automático ao cenário violado (pedido explícito: não avançar, destacar campos). +- **Atalhos no input focado**, não no “cenário ritual atual”. **Rejeitado:** atalhos sempre na média; atalhos no último editado sem foco visível. +- **Reuso visual da lista** — strip + grid compact. **Rejeitado:** novo componente wizard-only duplicando `VoteEstimateScenarioInputs`; input gigante central de B61. +- **Gravação batch no confirm** (igual B61). **Rejeitado:** três POSTs por cenário. +- **i18n:** ids `central`/`pessimistic`/`optimistic`; copy pt-BR nos labels existentes (`voteEstimateScenarioLabels`). + +## Questões em aberto + +- **Extrair editor compartilhado wizard+lista?** **Opções:** A) wizard monta `VoteEstimateScenarioStrip` + inputs inline | B) estender `VoteEstimateScenarioInputs` com `mode: 'autosave' | 'draft'` + `focusedScenario` + `violationScenarios`. **Recomendação:** B se o diff do wizard fica <~40 linhas de duplicação; A se o hook de draft da lista (debounce/autosave) contaminaria o wizard. _(assumido na implementação — validar no `/simplify`)_ + +## Abordagem proposta + +```mermaid +flowchart TD + A["Início: Ajustar votos"] --> B["B60: ?municipio=slug"] + B --> C["B77: WizardExpectedVotesStep
3 inputs + strip + atalhos"] + C --> D{Confirmar} + D -->|violação| E["Alert + ring nos inputs
sem navegação"] + E --> C + D -->|OK| F["POST batch expected-votes"] + F --> G["Mensagem sucesso + placeholder A1 / Início"] +``` + +Componentes: + +- **`WizardExpectedVotesStep`** (`src/components/campaign/shared/WizardExpectedVotesStep.tsx`): reescrever corpo — estado local `drafts` para os três cenários (mesma sanitização numérica que `VoteEstimateScenarioInputs`); `focusedScenario`; barra `VoteEstimateScenarioStrip` com `activeScenario={focusedScenario}` e `markerMode="active-only"` (média entre extremos); grid 3 inputs com highlight quando `violationScenarios` inclui o cenário; row de atalhos (`VOTE_SHORTCUTS` + `applyVoteShortcut` em `focusedScenario`); CTA confirm chama `getWizardVoteViolation` → POST ou erro inline. Remover props `currentScenario` e efeitos de troca de cenário. +- **`VoteEstimateScenarioInputs`** (`src/components/campaign/votePledge/VoteEstimateScenarioInputs.tsx`): opcional — exportar subpartes ou adicionar props `onFocusScenario`, `errorScenarios`, `commitMode: 'manual'` se B reduz duplicação sem puxar `useCampaignCellAutosave`. +- **`acoes/[slug]/page.tsx`**: para `atualizar-votos` + `municipio`, renderizar step sem `resolveWizardScenarioParam`; redirect canônico se só `cenario` na query. +- **`wizardVoteEstimate.ts`**: manter `applyVoteShortcut`, `getWizardVoteViolation`, `parseWizardVoteDraft`; **deprecar** uso de `getNextWizardVoteScenario`, `wizardVoteStepCtaLabel`, `WIZARD_VOTE_SCENARIO_EDIT_ORDER` no wizard (podem permanecer se testes/utilidades ainda referenciam — knip na entrega). +- **`campaignActionRoutes.ts`**: `wizardActionHref` — parar de emitir `?cenario=` para votos; links internos legados redirecionam. +- Sem migration, sem collection, sem server action nova. + +## Dependências + +- **B59** ✓ (chassis), **B60** ✓ (município), **B61** ✓ (endpoint batch + step existente), **B76** ✓ (lógica de violação — absorvida inline). +- Soft: **B75** (header mobile — título “Ajustar votos” + subtítulo município). +- Reuso: `VoteEstimateScenarioStrip`, `applyVoteShortcut`, `getWizardVoteViolation`, `MUNICIPALITY_EXPECTED_VOTES_ENDPOINT`, `CampaignWizardShell`. + +## Não escopo + +- Preview de cobertura E8 no passo → wiring A1 / lista (`MunicipalityListGoalCoverageCell`). +- Auto-save debounced estilo B27 na lista — lista mantém `useCampaignCellAutosave`; wizard mantém confirm explícito. +- Orquestrador A1 completo (sinal B63, tendência B64, resumo) → planos respectivos. +- Mudar invariante `pessimistic ≤ central ≤ optimistic` ou mensagens de servidor. + +## Rabbit holes + +- **Generalizar `VoteEstimateScenarioInputs` para wizard + lista + detalhe.** Se alguém “só completar”: props para autosave, foco, violação, popover vs full page — vira segundo design system. **Mitigação:** wizard copia o layout compact (strip + grid) e compartilha só strip + `applyVoteShortcut`; extrair só se `/simplify` medir duplicação >~60 linhas. +- **Reintroduzir `?cenario=` para deep-link.** Mitigação: redirect canônico sem param; um passo só. + +## Adiado com gatilho + +- **Animação da faixa ao mover foco entre inputs.** Revisitar se critique Impeccable pedir feedback além do marcador `active-only` (hoje suficiente para o pedido). + +## Referências + +- `docs/roadmap.md` — B77, UX-1 wizards +- `docs/plans/ajuste-votos-wizard.md` — ritual anterior (as-built B61) +- `docs/plans/corrigir-coerencia-wizard-ajuste-votos.md` — B76 (substituído por validação inline) +- `docs/plans/fluxos-acao-primeiro-inicio.md` — A1 +- `src/components/campaign/municipality/MunicipalityListExpectedVotesControl.tsx` — referência UX +- `src/components/campaign/votePledge/VoteEstimateScenarioInputs.tsx` — compact layout +- `src/components/campaign/votePledge/VoteEstimateScenarioStrip.tsx` — faixa relativa +- `src/components/campaign/shared/WizardExpectedVotesStep.tsx` — alvo da refatoração +- `src/lib/wizardVoteEstimate.ts` — atalhos e violação +- `src/lib/voteEstimate.ts` — invariante de ordem +- `tests/unit/wizardExpectedVotesStep.unit.spec.tsx`, `tests/e2e/campaignHomeActions.e2e.spec.ts` +- AGENTS.md — naming, sem migration, `overrideAccess: false` nos loaders diff --git a/docs/roadmap.md b/docs/roadmap.md index b52ebe09..4a68f722 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -1,6 +1,6 @@ # Roadmap — Teqo -Atualizado em: 2026-07-30 — ~~**B76**~~ ✓ coerência wizard ajuste votos; **B74** gap strip v2; **B75** header mobile do wizard; **B73** remover bottom nav; ~~**B66**~~ ✓ busca no focus; ~~**B71**~~ ✓ polish busca; ~~**B61**~~ ✓; ~~**B51**~~ ✓; ~~**B52**~~ ✓; ~~**B72**~~ ✓; ~~**B69**~~ ✓ liderança exclusiva (org fica); **B70** A4 liderança; ~~**B62**~~ ✓ simplificar sinal (E11 triage 1 por presença); ~~**B56**~~ ✓; ~~**B67**~~ ✓; ~~**B68**~~ ✓; ~~**B59**~~ ✓; ~~**B65**~~ ✓; ~~**B60**~~ ✓; ~~**B48**~~ ✓; ~~**B49**~~ ✓; ~~**B50**~~ ✓; B53–B55; wizards ~~B60~~ ✓/~~**B61**~~ ✓/~~**B76**~~ ✓/B63–B64. +Atualizado em: 2026-07-30 — **B77** simplificar wizard ajuste votos (3 cenários numa tela); ~~**B76**~~ ✓ coerência wizard ajuste votos; **B74** gap strip v2; **B75** header mobile do wizard; **B73** remover bottom nav; ~~**B66**~~ ✓ busca no focus; ~~**B71**~~ ✓ polish busca; ~~**B61**~~ ✓; ~~**B51**~~ ✓; ~~**B52**~~ ✓; ~~**B72**~~ ✓; ~~**B69**~~ ✓ liderança exclusiva (org fica); **B70** A4 liderança; ~~**B62**~~ ✓ simplificar sinal (E11 triage 1 por presença); ~~**B56**~~ ✓; ~~**B67**~~ ✓; ~~**B68**~~ ✓; ~~**B59**~~ ✓; ~~**B65**~~ ✓; ~~**B60**~~ ✓; ~~**B48**~~ ✓; ~~**B49**~~ ✓; ~~**B50**~~ ✓; B53–B55; wizards ~~B60~~ ✓/~~**B61**~~ ✓/~~**B76**~~ ✓/**B77**/B63–B64. 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) @@ -83,7 +83,7 @@ O discovery literatura→persona→entrevista ([relatório aprovado](research/re ### Demais itens abertos -- **UX-1 fluxos ação-primeiro (prioridade pós-sessão 29/07)** — evidência: [snapshot](plans/sessao-observada-coordenador-2026-07-29-snapshot.md) · O9–O11 em CUSTOMER.md · **rascunho:** [fluxos-acao-primeiro-inicio.md](plans/fluxos-acao-primeiro-inicio.md). Soft deadline: **03/08**. **Chassis Início:** ~~**B43**~~ ✓ → ~~**B44**~~ ✓ → ~~**B45**~~ ✓ → ~~**B46**~~ ✓ → ~~**B47**~~ ✓ → ~~**B58**~~ ✓ → ~~**B67**~~ ✓ (pan strip) → ~~**B65**~~ ✓ (âncora inferior mobile) → ~~**B66**~~ ✓ (focus limpa tela + animação) → ~~**B48**~~ ✓ → ~~**B49**~~ ✓ → ~~**B68**~~ ✓ → ~~**B71**~~ ✓ (polish resultados busca) · ~~**B72**~~ ✓ (gap strip) → **B74** (gap strip v2) → **B73** (remover bottom nav mobile) → ~~**B50**~~ ✓ → ~~**B51**~~ ✓ → ~~**B52**~~ ✓ → **B53–B55** · ~~**B56**~~ ✓ · **B57** futuro. **Wizards:** ~~**B59**~~ ✓ → **B75** (header mobile wizard) → ~~**B60**~~ ✓/~~**B61**~~ ✓/~~**B76**~~ ✓ /**B64** · ~~**B62**~~ ✓ → **B63** · ~~**B69**~~ ✓ → **B70** (A4 liderança). +- **UX-1 fluxos ação-primeiro (prioridade pós-sessão 29/07)** — evidência: [snapshot](plans/sessao-observada-coordenador-2026-07-29-snapshot.md) · O9–O11 em CUSTOMER.md · **rascunho:** [fluxos-acao-primeiro-inicio.md](plans/fluxos-acao-primeiro-inicio.md). Soft deadline: **03/08**. **Chassis Início:** ~~**B43**~~ ✓ → ~~**B44**~~ ✓ → ~~**B45**~~ ✓ → ~~**B46**~~ ✓ → ~~**B47**~~ ✓ → ~~**B58**~~ ✓ → ~~**B67**~~ ✓ (pan strip) → ~~**B65**~~ ✓ (âncora inferior mobile) → ~~**B66**~~ ✓ (focus limpa tela + animação) → ~~**B48**~~ ✓ → ~~**B49**~~ ✓ → ~~**B68**~~ ✓ → ~~**B71**~~ ✓ (polish resultados busca) · ~~**B72**~~ ✓ (gap strip) → **B74** (gap strip v2) → **B73** (remover bottom nav mobile) → ~~**B50**~~ ✓ → ~~**B51**~~ ✓ → ~~**B52**~~ ✓ → **B53–B55** · ~~**B56**~~ ✓ · **B57** futuro. **Wizards:** ~~**B59**~~ ✓ → **B75** (header mobile wizard) → ~~**B60**~~ ✓/~~**B61**~~ ✓/~~**B76**~~ ✓ → **B77** (ajuste votos numa tela) /**B64** · ~~**B62**~~ ✓ → **B63** · ~~**B69**~~ ✓ → **B70** (A4 liderança). - ~~**B43** Início em branco + Quadro~~ — **entregue 2026-07-29** (`/campanha` blank; staff dashboard em `/campanha/quadro`; liderança em `/campanha/contatos`; nav Início+Quadro / Início+Meus contatos; `requireCampaignPageActor` manda leader bloqueado → `/campanha/contatos`; sem migration) · [plano](plans/inicio-em-branco-quadro.md) - ~~**B44** Botão de ação do Início + lista horizontal~~ — **entregue 2026-07-29** (`CampaignHomeActionButton` + `CampaignHomeActionStrip`; Tooltip fine / long-press Drawer coarse; montado no Início por B45 ✓) · ~1d · Janela 1 · Impeccable C · [plano](plans/botao-acao-inicio-strip.md) - ~~**B45** Catálogo de ações por persona~~ — **entregue 2026-07-29** (`src/lib/campaignHomeActions.ts` + `CampaignHomeActions`; staff 6 ações com `uncovered-municipalities` → `?coverage=sem_assessor&sort=votos`; leader 2 com `my-contacts` → `/campanha/contatos`; demais inertes; ilha client por limite RSC↔ícones Lucide) · [plano](plans/catalogo-acoes-inicio-por-persona.md) @@ -92,7 +92,8 @@ O discovery literatura→persona→entrevista ([relatório aprovado](research/re - **B59** ~~Chassis do wizard~~ — **entregue 2026-07-29** (`CampaignWizardShell`; `/campanha/acoes/*` com layout staff-only; Voltar `md:hidden`; caption sticky de município; catálogo B45 com hrefs; passo ponte até B60) · [plano](plans/chassis-wizard-campanha.md) - **B60** ~~Etapa busca município nos wizards~~ — **entregue 2026-07-29** (`WizardMunicipalitySearchStep`; `searchStaffMunicipalityHits` + modo `wizard-municipality` no `POST /campanha/home-search`; select → `?municipio=`; stub B61 com caption sticky; chips recentes adiados — sem `recentVisits` no repo) · [plano](plans/busca-municipio-wizard.md) - **B61** ~~Etapa ajuste de votos (média→pessimista→otimista; atalhos 2×/±50/±100; coerência com jump+warning)~~ — **entregue 2026-07-29** (`WizardExpectedVotesStep`; `?cenario=`; batch POST expected-votes ao concluir otimista; stub B60 substituído só em `atualizar-votos`) · [plano](plans/ajuste-votos-wizard.md) · ~~regressão jump+warning → B76~~ ✓ -- **B76** ~~Corrigir coerência do wizard de ajuste de votos~~ — **entregue 2026-07-30** (`useEffect` preserva `violationEditedScenario` no jump ao cenário violado; limpa ao editar draft/atalho; unit `wizardExpectedVotesStep.unit.spec.ts`) · Depende de B61 ✓ · [plano](plans/corrigir-coerencia-wizard-ajuste-votos.md) +- **B76** ~~Corrigir coerência do wizard de ajuste de votos~~ — **entregue 2026-07-30** (`useEffect` preserva `violationEditedScenario` no jump ao cenário violado; limpa ao editar draft/atalho; unit `wizardExpectedVotesStep.unit.spec.ts`) · Depende de B61 ✓ · [plano](plans/corrigir-coerencia-wizard-ajuste-votos.md) · UX de três passos → **B77** +- **B77** Simplificar wizard ajuste de votos — três cenários na mesma tela (strip + inputs como popover da lista); atalhos no input focado; coerência só no confirm (warning + highlight, sem `?cenario=` jump) · Depende de B61 ✓ / B76 ✓ · soft B75 · ~0,75–1d · Janela 1 · Paralelizável com B63/B64 · Impeccable B · [plano](plans/simplificar-ajuste-votos-wizard.md) - ~~**B62** Simplificar modelo de sinal~~ — **entregue 2026-07-29** (sinal = tipo + texto; migration drop `signal_source`/`triangulated`; labels mesa sem "broker" — `Rede esfriou`/`Adversário apareceu`/`Alguém pediu algo`; E11 sem dimensão triangulado: presença = confirmado, **triage 1**; forms/lista/detalhe/dossiê alinhados; e2e stale-B43 corrigido de bônus) · [plano](plans/simplificar-modelo-sinal.md) - **B63** Wizard registrar sinal (grid de tipos + Drawer info + texto + “Pular” se origem ≠ register-signal) · Depende de B59 + B62 · soft B60 · ~1–1,25d · Janela 1 · Impeccable C · [plano](plans/wizard-registro-sinal.md) - **B64** Wizard mudar tendência (grid colorido ≠ atual + Drawer info + justificativa com prefill/Limpar/Salvar + “Pular” sempre) · Depende de B59 · soft B60 · soft B24 ✓ · ~1–1,25d · Janela 1 · Impeccable C · [plano](plans/wizard-mudar-tendencia.md) @@ -248,6 +249,7 @@ flowchart TD B60n["B60 Busca município
nos wizards"] B61n["B61 ✓ Ajuste votos
cenários + atalhos"] B76n["B76 ✓ Coerência wizard
ajuste votos"] + B77n["B77 Ajuste votos
3 cenários 1 tela"] B62n["B62 ✓ Simplificar
modelo de sinal"] B63n["B63 Wizard sinal
tipo + texto"] B64n["B64 Wizard tendência
escolha + justificativa"] @@ -378,6 +380,9 @@ flowchart TD B66n -.paridade focus.-> B60n B60n -.município.-> B61n B61n -.regressão jump.-> B76n + B61n -.simplificar UX.-> B77n + B76n -.validação inline.-> B77n + B75n -.chrome mobile.-> B77n B60n -.município standalone.-> B63n B60n -.município standalone.-> B64n B61n -.prefill votos.-> B64n @@ -476,11 +481,11 @@ flowchart TD B8F2 -.rank por ZE = débito aberto.-> B14n ``` -Paralelizáveis agora: **B73** (remover bottom nav — paralelo a B74); **B74** (gap strip v2 — após ~~B72~~ ✓; paralelo a B73); **B75** (header mobile wizard — após ~~B59~~ ✓; soft nos wizards restantes B63/B64/B70); **B49–B53** após B47 ✓ / B48 ✓ (depois B54/B55). Após **B59**: ~~**B60**~~ ✓ ∥ ~~**B61**~~ ✓ ∥ **B64** (B75 soft). **B63** espera B59+B62 ✓. **B70** espera ~~B59+B69~~ ✓ (+ soft B75). Prefills ricos de B64 com B61/B63 são soft. **B57** futuro (depende B56 ✓). Fill-ins / débitos `*+` e itens já ✓ permanecem fora do caminho crítico UX-1. **D3** só após smoke + folga se UX-1 não absorver o Zap. +Paralelizáveis agora: **B73** (remover bottom nav — paralelo a B74); **B74** (gap strip v2 — após ~~B72~~ ✓; paralelo a B73); **B75** (header mobile wizard — após ~~B59~~ ✓; soft nos wizards restantes B63/B64/B70/B77); **B77** (simplificar ajuste votos — após ~~B61~~ ✓/~~B76~~ ✓; paralelo a B63/B64); **B49–B53** após B47 ✓ / B48 ✓ (depois B54/B55). Após **B59**: ~~**B60**~~ ✓ ∥ ~~**B61**~~ ✓ ∥ **B64** (B75 soft). **B63** espera B59+B62 ✓. **B70** espera ~~B59+B69~~ ✓ (+ soft B75). Prefills ricos de B64 com B61/B63 são soft. **B57** futuro (depende B56 ✓). Fill-ins / débitos `*+` e itens já ✓ permanecem fora do caminho crítico UX-1. **D3** só após smoke + folga se UX-1 não absorver o Zap. ### Sequência por janela (só pendentes) -**Janela 1 — agora → 05/08 (convenções):** **prioridade pós-29/07 — ~~B43–B48~~ ✓ → ~~B67~~ ✓ (pan strip) **→ ~~B65~~ ✓** (âncora inferior) **→ ~~B66~~ ✓** (focus limpa busca) **→ ~~B68~~ ✓\*\* **→ ~~B71~~ ✓** (polish resultados) **· ~~B72~~ ✓** / **B74** (gap strip) **→ B73** (remover bottom nav) **→** **wizards ~~B59~~ ✓ → B75** (header mobile) → ~~B60~~ ✓/~~B61~~ ✓/~~B76~~ ✓ ∥B64 · ~~B62~~ ✓ → B63 · ~~B69~~ ✓ → B70 (soft 03/08) · **B49–B55** · [fluxos](plans/fluxos-acao-primeiro-inicio.md); smoke + onboarding Onda 0; **E4R ✓** aplicar seed em produção após smoke; demais entregues/pendentes da janela inalterados no detalhe abaixo — foco operacional = polish Início B74 + chrome B73/B75 + busca B49+ + wizards B63–B64 + A4 ~~B69~~ ✓/B70. +**Janela 1 — agora → 05/08 (convenções):** **prioridade pós-29/07 — ~~B43–B48~~ ✓ → ~~B67~~ ✓ (pan strip) **→ ~~B65~~ ✓** (âncora inferior) **→ ~~B66~~ ✓** (focus limpa busca) **→ ~~B68~~ ✓\*\* **→ ~~B71~~ ✓** (polish resultados) **· ~~B72~~ ✓** / **B74** (gap strip) **→ B73** (remover bottom nav) **→** **wizards ~~B59~~ ✓ → B75** (header mobile) → ~~B60~~ ✓/~~B61~~ ✓/~~B76~~ ✓ → **B77** (ajuste votos 1 tela) ∥B64 · ~~B62~~ ✓ → B63 · ~~B69~~ ✓ → B70 (soft 03/08) · **B49–B55** · [fluxos](plans/fluxos-acao-primeiro-inicio.md); smoke + onboarding Onda 0; **E4R ✓** aplicar seed em produção após smoke; demais entregues/pendentes da janela inalterados no detalhe abaixo — foco operacional = polish Início B74 + chrome B73/B75 + busca B49+ + wizards B77/B63–B64 + A4 ~~B69~~ ✓/B70. **Janela 2 — 05/08 → 16/08 (pré-propaganda):** C2 dados reais assim que o jurídico liberar; ~~**E9** fila de alocação~~ (entregue 2026-07-24, antecipada da janela 2); ~~**E16** dossiê do município~~ (entregue 2026-07-25, antecipado da janela 2); D2 se sobrar folga. @@ -490,7 +495,7 @@ Paralelizáveis agora: **B73** (remover bottom nav — paralelo a B74); **B74** ### Cortes seguros / não cortáveis -**Não cortáveis:** Onda 0 (jurídico/Consent); ~~**B43–B48** chassis UX-1 + busca Municípios~~ ✓; ~~**B67** pan na strip de ações~~ ✓ (entregue 2026-07-29 — B58 escondia a barra; drag fine restaurado); ~~**B65** âncora inferior mobile~~ ✓ (entregue 2026-07-29); ~~**B66** focus limpa busca~~ ✓ (entregue 2026-07-30); **B59–B61** etapas mínimas do wizard de votos (chassis + município sticky + busca município + ajuste — cortar = ações do Início continuam inertes no ritual A1 **e** o CG perde o contexto de município no meio do passo); ~~**B76** coerência do ajuste de votos~~ ✓ (entregue 2026-07-30); **B75** header mobile do wizard (cortar = Voltar+título no conteúdo competem com Mandate Red+sidebar no meio do ritual — emenda explícita a B59); ~~**B62** simplificar sinal~~ ✓ (entregue 2026-07-29); ~~**B69** simplificar liderança~~ ✓ (entregue 2026-07-30 — `exclusive` default true; setor/`consentNote` dropados; **organização fica**: ramificação política, ex. SindMed); ~~**E4R** seed da planilha~~ (entregue 2026-07-24 — ainda aplicar em produção após smoke); ~~**E8** conta da cadeira~~ (entregue 2026-07-24); ~~**C12** registro-fundação~~ (entregue 2026-07-24); C2 dados reais; assimetria declarado×estimado (relação de campo); ~~**E9** fila de alocação~~ (entregue 2026-07-24 — era o mínimo de "inteligência, não planilha"); ~~**B19** gerenciar assessores~~ (entregue 2026-07-24 — CG/candidato não entram em `/admin`); **B39** lembrar de mim / sessão longa (default de 2 h derruba sessão no onboarding/campo — atrito diário de adoção). +**Não cortáveis:** Onda 0 (jurídico/Consent); ~~**B43–B48** chassis UX-1 + busca Municípios~~ ✓; ~~**B67** pan na strip de ações~~ ✓ (entregue 2026-07-29 — B58 escondia a barra; drag fine restaurado); ~~**B65** âncora inferior mobile~~ ✓ (entregue 2026-07-29); ~~**B66** focus limpa busca~~ ✓ (entregue 2026-07-30); **B59–B61** etapas mínimas do wizard de votos (chassis + município sticky + busca município + ajuste — cortar = ações do Início continuam inertes no ritual A1 **e** o CG perde o contexto de município no meio do passo); ~~**B76** coerência do ajuste de votos~~ ✓ (entregue 2026-07-30); **B77** simplificar ajuste de votos (cortar = manter ritual pesado de três passos `?cenario=` que o CG pediu simplificar — emenda a B61); **B75** header mobile do wizard (cortar = Voltar+título no conteúdo competem com Mandate Red+sidebar no meio do ritual — emenda explícita a B59); ~~**B62** simplificar sinal~~ ✓ (entregue 2026-07-29); ~~**B69** simplificar liderança~~ ✓ (entregue 2026-07-30 — `exclusive` default true; setor/`consentNote` dropados; **organização fica**: ramificação política, ex. SindMed); ~~**E4R** seed da planilha~~ (entregue 2026-07-24 — ainda aplicar em produção após smoke); ~~**E8** conta da cadeira~~ (entregue 2026-07-24); ~~**C12** registro-fundação~~ (entregue 2026-07-24); C2 dados reais; assimetria declarado×estimado (relação de campo); ~~**E9** fila de alocação~~ (entregue 2026-07-24 — era o mínimo de "inteligência, não planilha"); ~~**B19** gerenciar assessores~~ (entregue 2026-07-24 — CG/candidato não entram em `/admin`); **B39** lembrar de mim / sessão longa (default de 2 h derruba sessão no onboarding/campo — atrito diário de adoção). **Cortes seguros** (se o prazo apertar, nesta ordem): ~~**B71**~~ ✓; **B74** gap strip v2 (B72 já apertou uma vez — cortar = strip um pouco esparça); **B68** sugestões na busca aberta (digitação B48 continua; cortar = empty mudo no focus até folga); **B64** wizard tendência standalone (A1 ainda pode embutir tendência via B24 ✓ / passo curto; cortar = sem grid colorido + prefill até folga); **B63** wizard sinal standalone (A1 ainda pode embutir select pós-B62; cortar = sem grid/Drawer de tipos até folga); **B70** wizard liderança standalone (A1 ainda pode embutir passo curto pós-B69; cortar = sem grid 2/3/4 até folga — pacote Cairu sofre); **B57** delta 7d (Início continua com total+cobertura do B56; Δ era futuro de propósito); **B56** resumo no Início (Quadro já tem os mesmos agregados — cortar = Início sem briefing numérico); **B55** ações WA/share na busca (linha continua clicável); **B54** grid/cap (empilhar sempre); **B51**/**B53**/**B52**/**B50**/**B49** grupos secundários da busca (nessa ordem — Municípios B48 fica); **D5** inbox→rascunhos (manter registro manual + `wa.me`); **D4** envio bridge (manter `wa.me`); **D3** fundação do canal (atalho `wa.me` continua); **E12** camada TI (rollup manual por lista; **E17** já dá a leitura regional básica no Início); **E13** planejador de giros (rebaixado na fila de corte em 2026-07-24: "perna"/agenda é a restrição dominante nomeada em campo — cortar só depois de E12; agenda segue manual com J-A/J-B como guia); **E15** backtest (pós-eleição por definição — cortar = perder o aprendizado 2030); **E11** motor v1 (manter a fila E9 ✓ sem sugestões); **B13** símbolo proporcional (manter quantis/LQ como escala); ~~**E14** níveis~~ (entregue 2026-07-27 — `priority` continua, intocado); ~~**B8 F2** polígonos~~ (entregue 2026-07-26 — o mapa não é mais agregado em Salvador); **D2** push (adiar); **A6**; ~~**B14** município mais próximo~~ (entregue 2026-07-26); ~~**B38** sidebar recolhido em tablet~~ (entregue 2026-07-26); ~~**B40** login biométrico~~ (entregue 2026-07-28 — senha + B39 ✓ continuam, e cortar teria custado só o atalho de digital/Face ID; era o mais caro e o primeiro a cair desta leva B39–B42); **B42** polimento mobile da lista de municípios (Popover e card longos continuam usáveis — cortar custa conforto de teclado e densidade no celular); ~~**B41** scroll horizontal + sticky~~ (entregue 2026-07-27) (a tabela continua legível com zoom/pan da página — cortar custa o overflow em tablet; cai **depois** de B42 porque o bug de layout em mesa é mais caro que o polish mobile); ~~**B18** filtros salvos~~ ✓ entregue (lista + filtros manuais continuam; Visitados cobrem “voltar”); **B21** página dos Territórios de Identidade (o painel do E17 ✓ no Início continua entregando a leitura regional) — **B25** cai junto por definição (sem destino, sem link); ~~**B17** seletor de colunas~~ ✓ entregue (tabela completa continua; scroll horizontal); ~~**B22** explicação no header~~ (entregue 2026-07-26); ~~**B23** tooltip nas células~~ (entregue 2026-07-26); ~~**B24** auto-save + justificativa na Tendência~~ (entregue 2026-07-26); **B27** combobox + chips nos Assessores (a lista de checkboxes com "Salvar" continua atribuindo — cortar custa a mesma coerência do B24 mais o tempo do CG procurando nomes numa coluna durante o onboarding; corte junto com B24, já que dividem o endpoint); **B28** e-mail + celular na lista de lideranças (a ficha `/liderancas/[id]` continua mostrando o contato — cortar custa o gesto de copiar/abrir ZAP na varredura; quick win barato de cortar e de fazer); ~~**B30** convite de completar cadastro por WhatsApp na lista~~ (entregue 2026-07-26); **B31** coluna de dobradinhas nas lideranças (o `RelationMultiSelect` da ficha interna continua vinculando — cortar custa o preenchimento em lote do onboarding, uma ficha aberta por liderança, e mantém o vínculo invisível na varredura; corte junto com B27, já que dividem a peça de célula, e antes de B29 só se a mesa já tiver preenchido as dobradinhas à mão); ~~**B32** editar Status de apoio na célula~~ (entregue 2026-07-26); **B34** chips editáveis de municípios na lista de lideranças (o texto simples `row.municipalityNames.join(', ')` continua legível e a ficha `/liderancas/[id]` continua vinculando município via formulário — cortar custa o link direto ao município, a navegação por chip e o atalho de adicionar/remover sem abrir a ficha; corte junto com B31, já que dividem a mesma peça de célula editável, e antes de B29 pelo mesmo motivo de coerência); **B37** chips editáveis de municípios na lista de dobradinhas (o número `municipalityCount` continua legível e a ficha de município continua vinculando dobradinha pelo `MunicipalityStrategyForm` — cortar custa o link direto, a navegação por chip e o atalho de adicionar/remover sem abrir a ficha do município; corte junto com B34, já que dividem a mesma lib e a mesma decisão de container); ~~**B36** coluna de lideranças na lista de dobradinhas~~ (entregue 2026-07-26; a contagem `leadershipCount` era o fallback e a ficha `/liderancas/[id]` continua vinculando dobradinha via formulário — cortar custa o link direto à liderança, a navegação por chip e o atalho de adicionar/remover sem abrir a ficha; corte junto com B31, já que dividem a mesma mutação/schema/lock); ~~**B33** sort + filtro no header das dobradinhas~~ (entregue 2026-07-26); **B29** sort + filtro no header das lideranças (a busca por nome continua funcionando e o escopo do assessor já reduz a lista — cortar custa o recorte por status/setor/município no onboarding da rede e mantém o header rico como cópia única de municípios; por ser o item mais caro desta faixa (~1,5–2 dias com a extração do head), cai **antes** do B26, que é o que sustenta o gate de adoção); **B26** registrar sinal na célula (o formulário completo em `/[slug]?tab=updates` continua sendo o caminho — cortar custa o laço da fila do E9 ✓ e o registro em campo pelo celular, que é justamente onde o gate de adoção se decide; por isso cai **depois** de B22/B23/B24); ~~**C13** renomear Plano de Ação → Atividade~~ (entregue 2026-07-25); **B20** prioritários no Início (lista `?priority=alta` continua; só perde o atalho — mas a seção atual mente, então cortar = conviver com o bug); fill-in **renomear coluna Cobertura** (a lista continua legível com o rótulo longo — corte = header mais largo); fill-in **remover coluna Tipo** (a lista continua completa; só mantém densidade morta — corte = conviver com a coluna); ~~fill-in **remover lista de zonas do Início**~~ ✓ (entregue 2026-07-28 — o mapa B8 F2 ✓ continua abrindo cada ZE); ~~**B16** filtros no header~~ (entregue 2026-07-25); ~~**B15** sort por coluna~~ (entregue 2026-07-24); ~~**A11**/**E17**~~ (entregues 2026-07-24); ~~**E18** documentação de conceitos~~ (entregue 2026-07-24); ~~**E16** dossiê~~ (entregue 2026-07-25); demais débitos/fill-ins.