From ee746b124085bb387d352b62b25f196a44d7a1b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Ign=C3=A1cio=20Fontoura=20Solla?= Date: Wed, 29 Jul 2026 22:02:07 -0300 Subject: [PATCH] docs: add B69/B70 leadership model and wizard plans Register roadmap items for leadership simplification (exclusive flag, field cleanup) and the A4 update-leadership wizard grid flow. --- docs/plans/fluxos-acao-primeiro-inicio.md | 28 +++-- docs/plans/simplificar-modelo-lideranca.md | 114 ++++++++++++++++++ docs/plans/wizard-atualizar-lideranca.md | 132 +++++++++++++++++++++ docs/roadmap.md | 19 ++- 4 files changed, 274 insertions(+), 19 deletions(-) create mode 100644 docs/plans/simplificar-modelo-lideranca.md create mode 100644 docs/plans/wizard-atualizar-lideranca.md diff --git a/docs/plans/fluxos-acao-primeiro-inicio.md b/docs/plans/fluxos-acao-primeiro-inicio.md index 0d6d06d7..6245a1a1 100644 --- a/docs/plans/fluxos-acao-primeiro-inicio.md +++ b/docs/plans/fluxos-acao-primeiro-inicio.md @@ -129,19 +129,18 @@ Chrome: seta **Voltar** só mobile (**B59**). Também passo embutido de A1. ## Fluxo A4 — Atualizar liderança -Cobre o pacote Cairu sem exigir que ele ache a ficha da pessoa primeiro. +Cobre o pacote Cairu sem exigir que ele ache a ficha da pessoa primeiro. Modelo: **B69** (`exclusive` default true; sem org/setor/`consentNote`). UI: **B70**. -| Passo | Pergunta | Nota | -| ----- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | -| 1 | Em qual município? | | -| 2 | O que você quer fazer? | **Trocar quem coordena** · **Mudar status de apoio** · **Anotar votos que a pessoa declara** · **Incluir nova liderança** | -| 3a | Quem? | Busca no município; se não achar → nome + telefone (create mínimo) | -| 3b | (ramo status) Novo status? | Engajado / … (labels atuais, plain language) | -| 3c | (ramo votos) Quantos votos essa pessoa declara? | `declaredVotes`; staff pode seguir com estimativa se quiser — **estimativa nunca como passo da liderança** | -| 3d | (ramo troca) Quem deixa de ser o principal / quem entra? | Observação interna (“mordeu a isca”, “secundário”) | -| 4 | Resumo → **Registrar** | | +| Passo | Pergunta | UI / nota | +| ----- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 1 | Em qual município? | Busca só municípios (**B60**; pular se veio de A1) | +| 2 | Quem? | Grid de tiles (2/3/4 cols) + tile **Adicionar**; status no canto; info/hover = `notes`; nome truncado por palavra; telefone∥e-mail (**B70**). Skip “Pular atualização de liderança →” se origem ≠ A4 | +| 3 | Editar / criar | Form curto: nome, celular, e-mail, status de apoio, observação, **apoio exclusivo**. Salvar → volta ao grid; se dirty, **Continuar** inferior direito. Focus no input principal ao abrir. Sem pledge aqui | +| 4 | Continuar / fim | Avança fluxo pai ou Início + toast (standalone) | -**Corte 03/08:** ramos “status” + “observação / papel” + busca; create completo e pledge rico podem ser v1.1. +**“Liderança é a dobradinha”:** reusar ficha `stateDeputy` + chips `stateDeputies` (B31 ✓) — sem campo novo. **Estimativa nunca neste fluxo.** + +**Corte 03/08:** grid + form curto (status/obs/exclusivo/create); pledge rico e atalho criar-dobradinha ficam fora. --- @@ -222,10 +221,13 @@ Não competir com os botões. 5. **B61** Ajuste votos (média→pessimista→otimista + atalhos + coerência) — [ajuste-votos-wizard.md](ajuste-votos-wizard.md) 6. **B62** Simplificar modelo de sinal (tipo+texto; labels mesa) — [simplificar-modelo-sinal.md](simplificar-modelo-sinal.md) 7. **B63** Wizard sinal (grid tipo + texto + pular) — [wizard-registro-sinal.md](wizard-registro-sinal.md) -8. **A1** wiring (orquestrar B60+B61 + “quer também?” sinal/tendência + resumo/CTA) + 7b. **B64** Wizard tendência — [wizard-mudar-tendencia.md](wizard-mudar-tendencia.md) + 7c. **B69** Simplificar modelo de liderança (`exclusive` + limpeza) — [simplificar-modelo-lideranca.md](simplificar-modelo-lideranca.md) + 7d. **B70** Wizard atualizar liderança (grid + form) — [wizard-atualizar-lideranca.md](wizard-atualizar-lideranca.md) +8. **A1** wiring (orquestrar B60+B61 + “quer também?” sinal/tendência/liderança + resumo/CTA) 9. **A6** — atalho já em B45 (`uncovered-municipalities`) 10. **A2** standalone = B60+B63 (se não ficou bom só embutido em A1) -11. **A4** curto · **A5** + edição de demanda · E11 → wizards · A3 standalone +11. **A4** = B60+B70 · **A5** + edição de demanda · E11 → wizards · A3 standalone = B60+B64 --- diff --git a/docs/plans/simplificar-modelo-lideranca.md b/docs/plans/simplificar-modelo-lideranca.md new file mode 100644 index 00000000..420c2779 --- /dev/null +++ b/docs/plans/simplificar-modelo-lideranca.md @@ -0,0 +1,114 @@ +# Simplificar modelo de liderança (exclusivo + limpeza) + +Status: rascunho +Atualizado em: 2026-07-29 +Item do roadmap: [docs/roadmap.md](../roadmap.md) (Trilha B, item B69 — UX-1 / A4) +Impeccable: B — schema + forms/lista/convite existentes; UI de grid do wizard = **B70** +Appetite: ~1–1,25 dia eng; uma migration (drop + `exclusive`); zod/collection/UI lista/forms/invite; access `OwnLeadership` +Responsável: — + +## Design (Impeccable) + +Âncoras: `PRODUCT.md` / `DESIGN.md` · tema `campaign`. Superfícies tocadas = ficha/lista/convite já no Field Desk — craft compacto, não shape de rota nova. + +Na implementação: craft compacto → critique → polish. + +Brief compacto (B, direção clara): + +- **Persona:** CG/assessor atualizando rede no ritual A4; formulário longo = abandono. +- **Job principal:** gravar só o que a mesa usa no dia a dia — contato, status, observação, exclusividade — sem setor/org/consentimento externo. +- **Estratégia de cor:** Restrained. +- **Edit where you see:** sim nas superfícies existentes (B32 status; forms `/liderancas`); exclusividade entra nos forms internos + no form do **B70**. +- **Anti-goals:** segundo cadastro de pessoa; dropar Consent do **convite** LGPD; inventar join exclusividade×município nesta fatia. + +## Dados → decisão → apresentação + +Dados: N/A como métrica nova — boolean categórico + remoção de campos. Decisão = “esta liderança ainda articula **só** conosco?” (sim/não) e menos atrito de cadastro. + +## Contexto + +`leadership` (`src/collections/Leadership.ts`) carrega hoje: `organizations`, `sector`/`sectorNotes`, `consentNote` (“Registro de consentimento **externo**”), além de `supportStatus`, `notes`, `stateDeputies`, municípios e o trio de Consent do **convite** (`consent` / `consentContentHash` / `consentedAt`). + +Pedido de produto (2026-07-29), no pacote A4 / Cairu ([fluxos-acao-primeiro-inicio.md](fluxos-acao-primeiro-inicio.md)): + +1. Conceito de parceria **exclusiva** (default = exclusivo): liderança que era a articuladora principal e passa a dividir votos com outro candidato deixa de ser exclusiva — e pode voltar. Não é “principal vs secundária” na v1 (ranking implícito); é **exclusividade do apoio**. +2. Remover da mesa os conceitos pouco usados: **organização**, **setor** (e notas de setor), **registro de consentimento externo** (`consentNote`). +3. “A própria liderança é a dobradinha” — `stateDeputy` hoje é entidade por **nome** (sem `Contact`); `leadership` tem `Contact` único. Não unificar collections neste item. + +Lista `/campanha/liderancas` já filtra/ordena por `sector` (B29 ✓); coluna Organizações é texto morto. Invite autofill ainda pede `sector`/`sectorNotes`. + +## Objetivos + +- Migration `pnpm migrate:create simplify_leadership_fields` (nome sugerido): + - **DROP** `organizations` (tabela de join), `sector`, `sector_notes`, `consent_note`. + - **ADD** `exclusive` boolean **NOT NULL DEFAULT true** (backfill implícito = todos nascem exclusivos — honesto com o default de produto). +- Collection + zod (`src/lib/schemas/leadership.ts`): espelhar; `exclusive` staff-managed como `supportStatus`/`notes`. +- Remover de create/internal forms, list columns/filters/sort de setor, badges de org no `MunicipalityLeadershipsPanel`, `organizationNames` / `organizationIDs` do view model. +- Invite: dropar `sector`/`sectorNotes` do schema + `CampaignInviteForm` + redemption profile write (não tocar `consent*` do LGPD do convite). +- Access: `getOwnEngagedLeadership` deixa de selecionar/expor `organizationIDs` (`municipalities.ts`); qualquer leitor de org via liderança engajada ajusta ou morre. +- `organizationData` counts “lideranças da org” → **zero path** ou remover a métrica se só existia via esse join (verificar call sites; não inventar proxy). +- Labels: `exclusive` → “Apoio exclusivo”; false → leitura “Não exclusivo” / badge discreto na lista (opcional neste item; B70 mostra no tile/form). +- Sem Consent novo. Sem UI de grid do wizard (**B70**). +- Unit/int: create default `exclusive: true`; update para `false`; filtros de setor removidos; invite sem setor. + +## Decisões travadas + +- **Campo `exclusive: boolean`, default `true`, no documento `leadership`.** **Rejeitado:** `principal`/`prioritaria` (sugerem ranking 1º/2º, não o relato “dividiu apoio”); exclusividade por par liderança×município nesta fatia (caro — join/metadata; o relato do CG é sobre a parceria com a campanha, não nuance A vs B). Gatilho abaixo se a mesma pessoa precisar ser exclusiva em um município e não em outro. +- **Dropar `organizations`, `sector`, `sectorNotes`, `consentNote`.** **Rejeitado:** deixar opcionais “por se acaso” (pedido: simplificar; campos mortos travam atualização); dropar o trio `consent*` do **convite** (é LGPD `lideranca-autopreenchimento`, Onda 0 — distinto do `consentNote` externo). +- **“Liderança é a dobradinha” = reusar `stateDeputy` + `leadership.stateDeputies`.** Criar/vincular a ficha de dobradinha com o nome da pessoa; chips B31 ✓ já expressam a rede. **Rejeitado:** collection unificada; `Contact` obrigatório em `stateDeputy` agora; flag `actsAsStateDeputy` sem entity (segunda verdade). Atalho “criar dobradinha a partir desta liderança” → Adiado. +- **i18n:** identifier `exclusive`; copy “Apoio exclusivo” / “Não exclusivo”. + +## Questões em aberto + +- **Badge “Não exclusivo” na lista `/liderancas` neste item ou só no B70?** **Opções:** A lista + forms | B só forms/wizard. **Recomendação:** A — coluna ou chip discreto na lista (1 célula), senão o campo some da varredura. _(assumido)_ +- **Exclusividade por município?** **Opções:** A documento (v1) | B join. **Recomendação:** A; revisitação no Adiado. _(assumido — validar com produto se Cairu tiver o mesmo nome em dois municípios com papéis diferentes)_ + +## Abordagem proposta + +```mermaid +flowchart LR + Mig["migration DROP + exclusive"] --> Zod["zod + Leadership.ts"] + Zod --> UI["forms + lista + invite"] + Zod --> Access["OwnLeadership sem orgs"] + Zod --> Orgs["organizationData counts"] +``` + +Componentes: + +- Migration + `Leadership.ts` + `src/lib/schemas/leadership.ts` (+ invite schema se setor sair). +- `LeadershipForm` / `LeadershipInternalForm` — remover org/setor/consentNote; toggle `exclusive` (default on). +- `liderancas/page.tsx` + `leadershipListUrl` / filters — dropar `sector`; opcional coluna/filtro `exclusive`. +- `leadershipData.ts` — view models sem org/setor; incluir `exclusive`. +- `CampaignInviteForm` + redemption — sem setor. +- `municipalities.ts` `getOwnEngagedLeadership` — só municípios. +- **Migration:** sim (drop + add boolean default true). + +## Dependências + +- Nenhuma de outro plano aberto. Soft: B29 ✓ / B31 ✓ / B32 ✓ (superfícies a limpar). Desbloqueia **B70**. + +## Não escopo + +- Grid de tiles + form do wizard A4 → **B70**. +- Wiring “quer também liderança?” no A1 → fatia A1. +- Unificar `stateDeputy` com `Contact` / atalho criar-dobradinha → Adiado. +- Collection `organization` em si (vertical `/campanha/organizacoes` permanece; só some o vínculo na liderança). + +## Rabbit holes + +- **Exclusividade por município + histórico de mudanças.** **Mitigação:** boolean no doc; nota em `notes` se precisar matizar. +- **Reescrever access de leader via organizações.** **Mitigação:** remover o ramo; leader continua lockdown em contatos — confirmar que `organizationIDs` não alimenta rota staff crítica antes de dropar. +- **Backfill `exclusive: false` a partir de texto em `notes`.** **Mitigação:** não — default true; mesa corrige no wizard. + +## Adiado com gatilho + +- **Exclusividade por município.** Revisitar quando a mesma liderança tiver papéis opostos em ≥2 municípios no mesmo ritual. +- **Atalho “Criar / vincular dobradinha” a partir da ficha/wizard.** Revisitar quando B70 shippar e a mesa pedir o gesto ≥2× na mesma semana. +- **`stateDeputy.contact` opcional.** Revisitar se dedupe nome↔pessoa virar dor (E4R / onboarding). + +## Referências + +- [fluxos-acao-primeiro-inicio.md](fluxos-acao-primeiro-inicio.md) (A4) · [simplificar-modelo-sinal.md](simplificar-modelo-sinal.md) (precedente B62) · [autosave-status-lista-liderancas.md](autosave-status-lista-liderancas.md) · [ordenacao-filtros-lista-liderancas.md](ordenacao-filtros-lista-liderancas.md) +- `src/collections/Leadership.ts` · `src/lib/schemas/leadership.ts` · `src/utilities/leadership/*` · `src/utilities/access/municipalities.ts` · `CampaignInviteForm.tsx` +- AGENTS.md — migrations; Contact único; Consent por chave (convite intacto) +- `PRODUCT.md` / `DESIGN.md` diff --git a/docs/plans/wizard-atualizar-lideranca.md b/docs/plans/wizard-atualizar-lideranca.md new file mode 100644 index 00000000..1c96349c --- /dev/null +++ b/docs/plans/wizard-atualizar-lideranca.md @@ -0,0 +1,132 @@ +# Wizard atualizar liderança — grid + formulário curto + +Status: rascunho +Atualizado em: 2026-07-29 +Item do roadmap: [docs/roadmap.md](../roadmap.md) (Trilha B, item B70 — UX-1 wizards / A4) +Impeccable: C — UI nova (grid de lideranças + formulário de edição/criação no chassis B59) +Appetite: ~1,25–1,5 dia eng; 2 “modos” no B59 (grid → form); writes via actions `leadership` existentes; focus + skip + Continuar +Responsável: — + +## Design (Impeccable) + +Âncoras: `PRODUCT.md` / `DESIGN.md` · `Drawer`/`Tooltip` shadcn · tema `campaign` · sibling visual **B63**/**B64** (tiles) · focus policy do Field Desk. + +Na implementação: shape → craft → critique → polish. + +Brief compacto: + +- **Persona:** CG/assessor no polegar (pacote Cairu); veio de “Atualizar liderança” ou do “Quer ajustar liderança?” do A1; Zap pode interromper. +- **Job principal:** ver a rede do município num grid, abrir a ficha curta de quem mudou (ou criar), gravar, e **continuar** o fluxo pai — ou pular se a liderança não era o motivo. +- **Estratégia de cor:** Restrained — tiles como B63 (fundo transparente / borda); status de apoio no canto (badge outline / cor já usada em `SupportStatusBadge`); “Não exclusivo” como exceção visual discreta (não competir com status). +- **Edit where you see:** não no grid (toque = abrir form); no form, **Salvar** explícito (exceção nota/confirmação). Sem spreadsheet mode. +- **Anti-goals:** formulário longo (org/setor/consentNote — mortos no **B69**); abrir `/liderancas/[id]/editar` no meio do ritual; auto-save silencioso no form do wizard; esconder “Adicionar” atrás de menu. + +## Dados → decisão → apresentação + +Dados: N/A como métrica — catálogo de pessoas do município + campos de ficha. Decisão = quem atualizar / criar / pular; exclusividade e status são leitura+escrita no form (**B69**). + +## Contexto + +Ação **A4** ([fluxos-acao-primeiro-inicio.md](fluxos-acao-primeiro-inicio.md)) e passo 7 opcional de A1. Catálogo B45 ✓ já tem `update-leadership`. Chassis **B59**; município via **B60** (standalone) ou pré-escolhido (embutido). Modelo limpo + `exclusive` em **B69**. + +Pedido (2026-07-29) — substitui o rascunho A4 “ramos de intenção” por: + +**Tela 1 — grid de lideranças do município** + +- Grid: **2 col mobile / 3 tablet / 4 desktop**. +- Cada liderança = botão **quadrado grande**, cantos arredondados: + - **Status** (supportStatus) no canto **superior esquerdo**. + - Ícone/botão **informação** no canto **superior direito** → conteúdo de `notes` (observação); no **hover** do tile inteiro (e nos do mesmo tipo), o mesmo conteúdo do info (Tooltip fine / long-press ou title pattern já usado na strip — não só no ícone). + - **Título** = nome (cortar na última palavra que couber inteira — ellipsis por palavra, não mid-word). + - Abaixo, discreto: **telefone** (prioridade) ou e-mail se sem telefone. +- Um tile = **Adicionar nova** → form vazio. +- Clique no tile de pessoa → form de edição (fluxo raro vs. só olhar; ainda assim é o caminho de escrita). +- Topo direito: **“Pular atualização de liderança →”** quando a origem ≠ `update-leadership` (mesma regra de B63). +- Após Salvar no form com mudança: volta ao grid e aparece **“Continuar”** no canto **inferior direito** (avança o fluxo pai / commit do bloco). Sem dirty → sem Continuar (ou Continuar desabilitado — preferir **oculto** até dirty). + +**Tela 2 — formulário único (editar / criar)** + +- Campos: **Nome**, **Celular**, **E-mail**, **Status de apoio**, **Observação** (`notes`), **Apoio exclusivo** (`exclusive` — B69). +- Salvar → volta ao grid; create vincula ao município corrente (piso ≥1 município). +- Focus: ao abrir o form (e ao montar qualquer view nova do wizard com input), **focar automaticamente o input principal** (Nome no create; primeiro campo editável no edit — tipicamente Nome ou Status se nome read-only por access). Mesma regra ao abrir Drawer/modal de info se houver campo — senão foco no título do drawer (precedente B42). + +Município sticky (**B59**) após escolha. + +## Objetivos + +- Passos no shell B59: `WizardLeadershipGridStep` + `WizardLeadershipFormStep` (ou um client island com modo `grid | form`). +- Loader: lideranças do município no escopo do ator (`loadMunicipalityLeaderships` / select enxuto: id, contact name/phone/email, supportStatus, notes, exclusive). +- Info/hover: `notes` truncado com scroll no Drawer se longo; empty = “Sem observação.” +- Truncamento de nome: helper puro em `lib/` (word-boundary ellipsis) + unit. +- Skip: trailing slot B59; **hidden** se `entryAction === 'update-leadership'`. +- Salvar edit: `updateLeadershipInternal` / action existente (contact name/phone/email + supportStatus + notes + exclusive); create: `createLeadership` mínimo + município atual. +- Dirty → `Continuar` no grid; Continuar **não** grava de novo — só avança (writes já no Salvar do form). +- Pending nos botões Salvar/Continuar (Feel the action). +- Sem migration (B69 já migrou). Sem Consent novo. +- Unit: truncamento; skip oculto no entry; dirty gate do Continuar. E2e smoke: grid → form → save → Continuar; add new foca Nome. + +## Decisões travadas + +- **Grid-first (não árvore de intenções do rascunho A4).** **Rejeitado:** passo “O que você quer fazer?” (trocar quem coordena / status / votos / incluir) — atrito extra; o tile + form curto cobrem status/obs/exclusivo/create; votos declarados ficam fora deste wizard (pledge = outro passo / lista). +- **Skip só se origem ≠ `update-leadership`.** **Rejeitado:** sempre (permitiria ação Início sem atualizar); nunca (quebra “quer também?” do A1). +- **Continuar só após dirty save.** **Rejeitado:** Continuar sempre (avança sem registro); auto-avançar no Salvar sem voltar ao grid (impede atualizar 2ª liderança no mesmo município — pacote Cairu). +- **Form curto = 6 campos (incl. exclusive).** **Rejeitado:** form completo da ficha interna; pledge/estimativa neste passo. +- **Focus automático no input principal ao entrar na view.** **Rejeitado:** só no create; depender de focus nativo do browser sem `autoFocus`/`requestFocus` no mount. +- **i18n:** `entryAction`, `exclusive`; copy “Pular atualização de liderança →” / “Continuar” / “Adicionar liderança”. + +## Questões em aberto + +- **Após Continuar no standalone A4: Início ou resumo?** **Opções:** A Início + toast | B tela resumo A1-like. **Recomendação:** A no v1; B quando o wiring A1 unificar commit. _(assumido)_ +- **Nome do contact é editável no wizard para staff?** **Opções:** A sim (pedido) | B só admin Payload. **Recomendação:** A via action staff que já atualiza Contact nos forms internos — reusar o mesmo caminho; se access bloquear, falhar fechado com mensagem. _(assumido)_ + +## Abordagem proposta + +```mermaid +flowchart TB + Entry["origem: update-leadership | embutido A1"] --> Grid["grid lideranças + Adicionar"] + Grid -->|info/hover| Notes["notes"] + Grid -->|tile pessoa| Form["form curto"] + Grid -->|Adicionar| FormEmpty["form vazio"] + Form & FormEmpty -->|Salvar| Action["create/update leadership"] + Action --> GridDirty["grid + Continuar"] + Grid & Form -->|skip se permitido| Skip["próximo / Início"] + GridDirty -->|Continuar| Next["fluxo pai / Início"] +``` + +Componentes: + +- **`WizardLeadershipGridStep` / `WizardLeadershipFormStep`** em `components/campaign/leadership/` (ou `shared/` se o tile virar 3º call site com B63 — **não** extrair tile genérico no 2º; depth check: copiar padrão B63 até o 3º). +- **`truncateNameAtWordBoundary`** em `src/lib/` + unit. +- Reuso: B59 trailing skip + sticky município; `SupportStatusBadge` / labels; actions em `campanha/actions/leadership.ts`; Drawer info. +- Focus helper: `useFocusOnMount(ref)` se já existir no campaign shell; senão um hook mínimo no passo (não lib global prematura). +- **Migration:** Sem migration (depende de B69). + +## Dependências + +- Dura: **B59**, **B69**. Soft: **B60** (município standalone). Soft: B45 ✓ (`update-leadership`). Soft: B32 ✓ (labels/status). + +## Não escopo + +- Schema exclusive + drop org/setor/consentNote → **B69**. +- DeclaredVotes / estimativa no wizard → lista de pledges / passo futuro; **estimativa nunca neste fluxo**. +- Wiring completo A1 “Registrar atualização” → fatia A1. +- Abrir ficha completa `/liderancas/[id]`. + +## Rabbit holes + +- **Tile genérico compartilhado B63/B64/B70.** **Mitigação:** terceiro call site = gatilho; até lá copiar o padrão visual. +- **Optimistic multi-edit sem Salvar.** **Mitigação:** Salvar explícito no form; Continuar só navega. +- **Busca no grid quando N > ~20.** **Mitigação:** v1 sem busca; Adiado se município com rede grande doer. + +## Adiado com gatilho + +- **Busca/filtro no grid.** Revisitar se municipio típico do CG tiver >12 lideranças no loader. +- **Ramo “anotar votos declarados”.** Revisitar quando A1 pedir pledge no mesmo ritual (hoje fora do form curto). +- **Extrair `WizardChoiceTile`.** Revisitar no 3º grid irmão (já há B63+B64; este é o 3º — **pode** extrair neste item se o diff medir ≥~80 linhas duplicadas; senão defer explícito no `/simplify`). + +## Referências + +- [simplificar-modelo-lideranca.md](simplificar-modelo-lideranca.md) · [wizard-registro-sinal.md](wizard-registro-sinal.md) · [wizard-mudar-tendencia.md](wizard-mudar-tendencia.md) · [chassis-wizard-campanha.md](chassis-wizard-campanha.md) · [fluxos-acao-primeiro-inicio.md](fluxos-acao-primeiro-inicio.md) · [catalogo-acoes-inicio-por-persona.md](catalogo-acoes-inicio-por-persona.md) +- `campaignHomeActions.ts` (`update-leadership`) · `actions/leadership.ts` · `leadershipData.ts` · `LeadershipForm.tsx` +- AGENTS.md — `overrideAccess: false`; Contact; Feel the action +- `PRODUCT.md` / `DESIGN.md` · focus / B42 `initialFocus` diff --git a/docs/roadmap.md b/docs/roadmap.md index acceb79e..22bc7bc1 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -1,6 +1,6 @@ # Roadmap — Teqo -Atualizado em: 2026-07-29 — ~~**B56**~~ ✓ resumo no Início; ~~**B67**~~ ✓ pan na strip; **B68** sugestões na busca aberta; **B66**; **B59** sticky; ~~**B58**~~ ✓; **B65**; B48 ✓; B49–B55; wizards B59–B63. +Atualizado em: 2026-07-29 — **B69**–**B70** A4 liderança (exclusivo+limpeza → wizard grid); ~~**B56**~~ ✓; ~~**B67**~~ ✓; **B68**; **B66**; **B59** sticky; ~~**B58**~~ ✓; **B65**; B48 ✓; B49–B55; wizards B59–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**~~ ✓ → **B68** (sugestões empty/focus por papel) → **B49–B55** · ~~**B56**~~ ✓ (resumo) · **B57** futuro. **Wizards:** **B59** → **B60**/**B61**/**B64** · **B62** → **B63**. +- **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**~~ ✓ → **B68** (sugestões empty/focus por papel) → **B49–B55** · ~~**B56**~~ ✓ (resumo) · **B57** futuro. **Wizards:** **B59** → **B60**/**B61**/**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) @@ -95,6 +95,8 @@ O discovery literatura→persona→entrevista ([relatório aprovado](research/re - **B62** Simplificar modelo de sinal (dropar Fonte/Triangulado; labels mesa sem “broker”; migration + E11) · Depende de C12 ✓ · ~0,75–1d · Janela 1 · Impeccable B · [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) +- **B69** Simplificar modelo de liderança (`exclusive` default true; dropar org/setor/`consentNote`; dobradinha-self = `stateDeputy`+chips) · Depende de — · ~1–1,25d · Janela 1 · Paralelizável com B59/B62 · Impeccable B · [plano](plans/simplificar-modelo-lideranca.md) +- **B70** Wizard atualizar liderança (grid 2/3/4 + form curto + skip + Continuar dirty + focus) · Depende de B59 + B69 · soft B60 · ~1,25–1,5d · Janela 1 · Impeccable C · [plano](plans/wizard-atualizar-lideranca.md) - ~~**B46** Posição dos botões de ação (thumb zone)~~ — **entregue 2026-07-29** (`CampaignHomeLayout`: coluna `min-h-full` + spacer mobile; strip em `data-slot="home-actions"` na metade inferior do viewport; `md+` no topo; slot `searchSlot` + `data-slot="home-search"` para B47; sem migration) · [plano](plans/posicao-botoes-acao-inicio-thumb-zone.md) · **âncora inferior falhou em produção visual** → **B65** - ~~**B47** Busca global — input (debounce, sem botão; modo focado: sobe e esconde o resto)~~ — **entregue 2026-07-29** (`CampaignHomeStaffChrome` + `CampaignHomeSearch` + `useHomeSearchQuery`; debounce 250 ms; query ativa ≥2 chars; `HomeSearchContext` para B48+; staff-only via `isStaffCampaignRole`; Escape limpa; região `aria-live` sem hits ainda; sem migration) · [plano](plans/busca-global-inicio-input.md) · **emenda B66:** limpeza no **focus** (não só na digitação) + animação - **B66** Modo focado da busca no **focus** (não na digitação) + animação de retração do chrome (strip/spacer/resumo) como se a barra expandisse — emenda a decisão B47 “expand = query”; hits continuam gated por limiar ≥2 · Depende de B47 ✓ · soft B65 · ~0,5–0,75d · Janela 1 · Paralelizável com B49–B53 / B59 / B62 · Impeccable B · [plano](plans/modo-focado-busca-no-focus.md) · empty útil → **B68** @@ -236,6 +238,8 @@ flowchart TD B62n["B62 Simplificar
modelo de sinal"] B63n["B63 Wizard sinal
tipo + texto"] B64n["B64 Wizard tendência
escolha + justificativa"] + B69n["B69 Simplificar modelo
liderança + exclusive"] + B70n["B70 Wizard atualizar
liderança grid+form"] B15n["B15 ✓ Ordenar lista
por coluna"] B16n["B16 ✓ Filtros no header
da lista"] B17n["B17 ✓ Seletor de colunas
das 7 listas"] @@ -337,6 +341,9 @@ flowchart TD B59n --> B61n B59n --> B63n B59n --> B64n + B59n --> B70n + B69n --> B70n + B60n -.município standalone.-> B70n B48n -.UX/loader.-> B60n B66n -.paridade focus.-> B60n B60n -.município.-> B61n @@ -438,11 +445,11 @@ flowchart TD B8F2 -.rank por ZE = débito aberto.-> B14n ``` -Paralelizáveis agora: **B65** (âncora inferior mobile); **B66** (focus limpa busca — melhor após B65); **B68** (sugestões empty — após B48 ✓; melhor após B66); **B59** (chassis wizard — desbloqueia B60/B61/B63/B64); **B62** (simplificar sinal — paralelo a B59); **B49–B53** após B47 ✓ / B48 ✓ (depois B54/B55). Após **B59**: **B60** ∥ **B61** ∥ **B64**. **B63** espera B59+B62. 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: **B65** (âncora inferior mobile); **B66** (focus limpa busca — melhor após B65); **B68** (sugestões empty — após B48 ✓; melhor após B66); **B59** (chassis wizard — desbloqueia B60/B61/B63/B64/B70); **B62** (simplificar sinal — paralelo a B59); **B69** (simplificar liderança — paralelo a B59/B62; desbloqueia B70); **B49–B53** após B47 ✓ / B48 ✓ (depois B54/B55). Após **B59**: **B60** ∥ **B61** ∥ **B64**. **B63** espera B59+B62. **B70** espera B59+B69. 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** (sugestões empty) **→\*\* **wizards B59** → B60∥B61∥B64 · B62 → B63 (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 busca B65/B66/B68 + busca B49+ + wizards B59–B64. +**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** (sugestões empty) **→\*\* **wizards B59** → B60∥B61∥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 busca B65/B66/B68 + busca B49+ + wizards B59–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. @@ -452,9 +459,9 @@ Paralelizáveis agora: **B65** (âncora inferior mobile); **B66** (focus limpa b ### 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 (B46 ✓ não ancorou — cortar = thumb zone continua falhando no celular); **B66** focus limpa busca (cortar = strip compete com o gesto de buscar até digitar — emenda explícita de produto); **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); **B62** simplificar sinal (schema — cortar = wizard B63 e B26 seguem exigindo Fonte/Triangulado); ~~**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 (B46 ✓ não ancorou — cortar = thumb zone continua falhando no celular); **B66** focus limpa busca (cortar = strip compete com o gesto de buscar até digitar — emenda explícita de produto); **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); **B62** simplificar sinal (schema — cortar = wizard B63 e B26 seguem exigindo Fonte/Triangulado); **B69** simplificar liderança (schema `exclusive` + limpeza — cortar = A4/B70 sem modelo e ficha continua inchada); ~~**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): **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); **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. +**Cortes seguros** (se o prazo apertar, nesta ordem): **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. ## Bloqueadores atuais